Developer Advocate at Bearer.sh
Webhooks run a large portion of the “magic” that happens between applications. They are sometimes called reverse APIs, callbacks, and even notifications. Many services, such as SendGrid, Stripe, Slack, and GitHub use events to send webhooks as part of their API. This allows your application to listen for events and perform actions when they happen.
command you want to use may be
You can confirm the installed version by running the following from the terminal:
or, if that displays a version below 3:
What is a webhook
Webhooks are called reverse APIs for a reason. Instead of your application sending a request to the API, the API sends the request to your application. While the concept may sound different, the way that we consume webhooks is the same way that an API consumes a request.
In most web frameworks, there is the concept of a route. A route allows the application to respond with specific content or data when the user visits a specific URL. The same idea applies to APIs.
, the route is
is the name of the organization.
The same concept is applied when receiving webhooks. We establish a route, tell the service where to send the data, and our application sits and waits until a request comes in to that route. There are a few consistencies across webhook implementations.
- They are normally
- They receive JSON data.
- They need to respond quickly.
Receive a webhook with Flask
For our purposes, this is great as we are only concerned with routing. Make sure Python is installed, then run the following command in your terminal to install flask:
python -m pip install Flask
file, such as
and add the following:
from flask import Flask, request, Response app = Flask(__name__) @app.route('/webhook', methods=['POST']) def respond(): print(request.json); return Response(status=200)
before assigning it to the
variable. This naming scheme is convention in the Flask documentation.
decorator to listen for
requests made against the
path. This decorator calls the function that immediately follows it when a request is made to the route. In this case, that is the
out the request as json, then return a
with a status code of 200. This response tells the sender that we received the hook. You should be able to run the server using Flask’s preferred technique:
export FLASK_APP=main.py python -m flask run
requests made to the endpoint will trigger the
This is also the URL that you will provide the the service that sends the webhook.
Receive a webhook with Django
using the following command:
python -m pip install Django
utility to create a new project. If you already have an existing Django project that you want to add webhooks to, skip to the next step.
django-admin startproject example-project
This sets up the basis for a new Django project. Navigate to the newly created folder, and you should see a structure similar to the following:
example-project/ manage.py example-project/ __init__.py settings.py urls.py asgi.py wsgi.py
python manage.py runserver
django-admin startproject example-project .
with a trailing period (.) instead. For this tutorial, we’ll mirror the preferred way as shown previously.
To do that, we’ll set up an “app” called webhooks.
python manage.py startapp webhooks
This creates a new directory called webhooks. Great! Now we can write some code.
(not yet created), and
. Here we will write the logic for handling a route.
from django.http import HttpResponse from django.views.decorators.http import require_POST @require_POST def example(request): return HttpResponse('Hello, world. This is the webhook response.')
This code does the following:
- It imports the
object that will be used to send a response.
- It imports a special decorator to limit the request method. In Django, routes accept all HTTP methods by default and let the views manage which methods they respond to.
- Calls the decorator to limit the function that follows to only the
- Defines a function, named
that takes the request as an argument and returns a response.
function’s name will be linked to our
file shortly. It does not need to align to a specific path.
if it doesn’t already exist. This is where we organize routes within this sub-app of our project.
from django.urls import path from . import views urlpatterns = [ path('example/', views.example) ]
. It defines individual routes and associates them with views. We next import all views.
is passed a list of paths. This list is recognized by Django as the routes associated with the application.
and is associated with the view
, which was the name of our function in
. It should look similar to the previous file, but with an existing
route. Add a new path like so:
urlpatterns = [ path('admin/', admin.site.urls), path('webhooks/', include('webhooks.urls')) ]
python manage.py runserver
(replace the host and port with your own if they differ).
. Once deployed, append this path to the full URL and provide the full URL to the service that sends the webhook.
Testing webhooks locally
To test webhooks locally without deploying, we need to open up a connection from our development machine to the outside world.
./ngrok http 3000
in your terminal where 3000 is replaced with the port of your running Python application. For example, a default Django site often runs on port 8000.
Once up and running, ngrok will provide you with a url that you can use to test your webhook. In Bearer, we provide a “test” button in our Notification settings.
Once configured, you’ll start receiving webhooks at that URL. Don’t forget to change it over to the final, deployed webhook URL once development and testing are complete.
What can you do with this information?
Once a webhook is configured, it is up to you how to handle the information it receives. You can use this information to react to events in real-time, feature flip parts of your application, or even use it as a way to write data to a database. There are countless capabilities your webhook can have depending on what kind of information the API provider sends.
While we built a basic implementation in this article, it is worth mentioning that many services offer ways to validate that a request is coming from the actual source. This can be done by limiting the URLs that access your application, or by matching a secret key. GitHub, for example, allows you to set a secret that will be sent with each webhook.