Creating Grafana Annotations with Webhooks
Grafana is a great tool for visualizing data.
In this tutorial I’ll be putting annotations, or comments on points of interest overlayed on top of graph panels.

Creating Annotations on Graphs
To create annotations on graphs in Grafana, there are several components that need to be tied together:
- Grafana for the graph(s)
- A database / datasource for our annotations
- An application / webhook that listens for incoming data
The data flows as follows:
An event happens that creates some data that we want to become our annotation. This data needs to be parsed/massaged into a few variables to become an annotation. These values are:
- Start time
- End time
- Name
- Tags
- Description
- Annotation color
Here is an annotation example request:
1 |
|
Let’s break this request down. The first line is the URI Request. As you can see, it’s a POST request for /api/annotations We don’t request a .php or .html file because we use a rewrite rule in the webserver to remove this requirement. We’ll go into that later. The Request Headers are: Accept: application/json and Content-Type: application/json
Next is the request body. This consists of the dashboardId which is the dashboard that has the graph we want to annotate. It also has the panelId which is the actual graph panel we’re annotating. time is the start time of our event in Unix Epoch format. isRegion tells Grafana that this annotation is not just a point in time, but rather a range of time - referred to as a “region’. Since we’re defining a region we specify a timeEnd which is also in Unix Epoch format. Next we have tags. These tags are metadata and are useful for grouping. Finally, we have text which is the description or the annotation itself.
If the request is formatted properly and Grafana parses it successfully, it will send the following Response header:
1 | HTTP/1.1 200 |
If we break this down, we see the Response Header is HTTP/1.1 200 which is the Status-Line. The status-line has 2 parts: the protocol HTTP 1/1 and the status code 200 (Success).
Next is the response body. This consists of 3 json key/value pairs that are the message, the annotation id, and the endId.
Configuring Grafana
Annotations are only available on certain types of Grafana graphs. In this example, I’m using a standard ‘graph panel’. To configure Grafana, we’ll go into the Settings of our Dashboard and select Annotations:
The Annotation Datasource / Database

When Grafana renders a graph, it displays the data from the dashboard datasource (a database backend) and it displays the annotations over top of that graph at the appropriate points in time or over a time range.
The Webhook
For this example, I’m using PHP to create the webhook that will listen for incoming data. Once it receives the data that will become an annotation, it parses the data and inserts it into a database. In this example, I’m using InfluxDB. This database is mapped into Grafana as an Annotation Datasource.
Configuring The Webserver - nginx
Now we need to configure our webserver - nginx - for our webhook. The webhook is a .php script that will parse the request (header and body). It massages this data into a format suitable for our database and inserts it. For this example, our webhook endpoint is: /api/annotations (as seen in the request example above). To do this we’ll add the following location block to our /etc/nginx/nginx.conf file:
1 | location /api/ { |
Further Reading
Official Grafana Documentation on Annotations
Official Grafana Annotation API Documentation
Another blog post about Annotations
Technology used
![]() | ![]() |
Creating Grafana Annotations with Webhooks
https://chrisbergeron.com/2020/03/08/creating_grafana_annotations_with_webhooks/




