Recently, I was asked what was the difference between Webhooks and APIs. This was a question I also had a few years ago when I started programming. In this article, I will briefly explain what they are and give a simple tutorial on how you can use them.
What is an API?
API stands for Application Programming Interface. APIs allow applications to talk with each other via a common communication method. There are a lot of different API architectural styles such as REST, SOAP, GraphQL and gRPC. With most APIs, there’s a request followed by a response.
For example, a restaurant might have an application that would make an API request to their server and obtain a list of menu items in the response, then display it for their users. A lot applications out there provide public APIs that you can be use in your personal projects such as YouTube Data API and Google Map API.
What is a Webhook?
Unlike APIs, Webhook is simply an HTTP POST request that is triggered automatically when an event occurs. Basically, webhooks are “user-defined callbacks”.
For example, an application could provide a webhook that will get triggered by another application when new data is received (callback) instead of sending requests at fixed interval to fetch new data (polling).
Tutorial
Send Message Using Slack API with Slack Bot
Slack provides a complete list of REST API methods available to bots. We are going to use the users.list method to list available users and chat.postMessage method to send a message to a user or channel.
1. Navigate to the Custom Integrations page of your Workspace https://<your-workspace-name>.slack.com/apps/manage/custom-integrations
and select Bots
2. Choose a name and add the bot integration.
3. Save the API Token, we will use it later in Slack API requests for authentication.
4. Let’s try out the users.list method using an API client like Postman and click on code to generate code:
# slack-api.py
import requests, json
base_url = "https://slack.com/api"
payload={}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer [your Slack Bot API Token]'
}
# Make GET request and receive response
response = requests.request("GET", f"{base_url}/users.list", headers=headers, data=payload)
# Convert response to a Dict object
response_json = json.loads(response.text)
# Find user by username
username = 'yueh.liu'
user = next((member for member in response_json['members'] if member['name'] == username), None)
# Make sure the user exists
if not user:
raise Exception(f'User [{username}] was not found')
# Save the user_id
user_id = user['id']
5. Now that we have the User ID, we can try sending a message to that user! We can repeat the previous step with the chat.postMessage method. Make sure to change the request method to POST
.
You should receive a message like this on Slack
The updated code should look something like this:
# slack-api.py
import requests, json
base_url = "https://slack.com/api"
payload={}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer [your Slack Bot API Token]'
}
# Make GET request and receive response
response = requests.request("GET", f"{base_url}/users.list", headers=headers, data=payload)
# Convert response to a Dict object
response_json = json.loads(response.text)
# Find user by username
username = 'yueh.liu'
user = next((member for member in response_json['members'] if member['name'] == username), None)
# Make sure the user exists
if not user:
raise Exception(f'User [{username}] was not found')
# Save the user_id
user_id = user['id']
# Set the parameters such as the channel ID (user ID in our case), username for the bot, text message, icon url, etc
# You can also send a JSON payload instead of query parameters, but you would need to change the 'Content-Type' to 'application/json' in the headers
params = f"channel={user_id}&text=Hello Yueh!&username=ua-bot&icon_url=https://some-url-link.jpg"
# Make POST request and receive response
response = requests.request("POST", f"{base_url}/chat.postMessage?{params}", headers=headers, data=payload)
print(response.text)
Send Message Using Slack Incoming Webhooks
Incoming Webhooks are a simple way to post messages from external sources into Slack. They make use of normal HTTP requests with a JSON payload, which includes the message and a few other optional details described later.
For this example, we are going to create a Web Server and integrate an Incoming Webhook. We will trigger the webhook automatically to send a message to a user on Slack whenever the server receives a message.
1. Navigate to the Custom Integrations page of your Workspace https://<your-workspace-name>.slack.com/apps/manage/custom-integrations
and select Incoming WebHooks
2. Choose a channel (or user) to post your messages and add the webhook
You should see a message like this on Slack
4. Since webhooks work best as callback from a server, let’s write a simple HTTP server that runs on localhost and port 3000. The web server will receive a message on /message
path and read the message content from the payload.
# server.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
# Define a custom Request Handler
class CustomHandler(BaseHTTPRequestHandler):
def set_response(self, code, byte_message):
self.send_response(code)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(byte_message)
def do_GET(self):
if self.path == "/":
self.set_response(200, "I'm alive!!!\n".encode())
self.wfile.write()
else:
self.send_error(404)
return
def do_POST(self):
if self.path == "/message":
# Get payload
content_length = int(self.headers["Content-Length"])
encoded_data = self.rfile.read(content_length)
data = json.loads(encoded_data.decode("utf-8"))
if not "message" in data and not data['message']:
self.send_error(400, "Bad Request", '"message" must be in the payload')
return
self.set_response(200, f"Received message: \"{data['message']}\"\n".encode())
else:
self.send_error(404)
return
# Initialize an HTTP server
port = 3000
address = ("", port)
server = HTTPServer(address, CustomHandler)
# Start your server
print(f"Starting Web server on localhost:{port}..")
server.serve_forever()
6. Now that the server is running, let’s integrate the webhook into the code!
# server.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import json, requests
# Define a custom Request Handler
class CustomHandler(BaseHTTPRequestHandler):
def set_response(self, code, byte_message):
self.send_response(code)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(byte_message)
def do_GET(self):
if self.path == "/":
self.set_response(200, "I'm alive!!!\n".encode())
self.wfile.write()
else:
self.send_error(404)
return
def do_POST(self):
if self.path == "/message":
# Get payload
content_length = int(self.headers["Content-Length"])
encoded_data = self.rfile.read(content_length)
data = json.loads(encoded_data.decode("utf-8"))
if not "message" in data and not data['message']:
self.send_error(400, "Bad Request", '"message" must be in the payload')
return
self.set_response(200, f"Received message: \"{data['message']}\"\n".encode())
# Trigger the Webhook (make POST request) and we can ignore the response and failure
try:
webhook_url = "[Your Slack Webhook Url]"
headers = { 'Content-Type': 'application/json' }
payload = "{ \"text\": \"Your server received the following message:\n\n" + data['message'] + "\" }"
requests.request("POST", webhook_url, headers=headers, data=payload)
except Exception:
pass
else:
self.send_error(404)
return
# Initialize an HTTP server
port = 3000
address = ("", port)
server = HTTPServer(address, CustomHandler)
# Start your server
print(f"Starting Web server on localhost:{port}..")
server.serve_forever()
7. Once updated, we can re-send the same message as earlier and you should receive a message like this on Slack:
Conclusion
An API is a communication method used by applications to talk with other applications. Webhook is a POST request that is triggered automatically when an event happens. Basically, APIs are request-based while webhooks are event-based.
🐢