As part of SAS Viya Monitoring for Kubernetes, Grafana runs alongside Prometheus to keep an eye on a SAS Viya deployment. Grafana includes built‑in support for several alert “receivers” out of the box, so that notifications can be sent to administrators and engineers via email, Slack, Microsoft Teams, and more. It's also possible to use webhooks to configure additional applications and platforms as destinations for alert notifications. In this post, I will cover the steps required to be notified of alerts via WhatsApp, including important considerations and additional setup.
My existing deployment has SAS Viya deployed to a namespace called 'edu', and the monitoring stack of SAS Viya Monitoring for Kubernetes (including Grafana) deployed to a namespace called 'monitoring'. To send alerts to WhatsApp, some more setup is required. First, we need a way to send messages to WhatsApp - that is, a commercial service or platform that can send messages to users. For my demo, I used Twilio, which has a limited free tier, but offers a paid service better suited to enterprises and for Production use. Twilio (and other similar services - there are many) is a WhatsApp Business API provider. By default, however, WhatsApp's API isn't webhook friendly, so we also need something else that can relay the alert payload from Grafana. That's the second required part of the setup - a bridge service that accepts Grafana webhooks and forwards them to Twilio.
After creating an account in Twilio or your WhatsApp Business API provider platform, locate the account information required for the bridge service. In my case, I needed:
the Account SID – a value starting with AC.
the Auth Token – shown alongside the Account SID when you reveal it.
the WhatsApp sender number.
the recipient number(s). I used my own. You'll need to verify recipient numbers.
These values will be injected as environment variables into the bridge service. The bridge is a small HTTP service that accepts alert notifications from Grafana and calls (in my case) Twilio's WhatsApp API. I asked ChatGPT to write the Python code for me and create a docker container:
mkdir ~/bridge
cd ~/bridge
# Create two files inside this folder: app.py and requirements.txt
# ------ app.py ------
cat > app.py << 'EOF'
import os
import requests
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
ACCOUNT_SID = os.environ["ACCOUNT_SID"]
AUTH_TOKEN = os.environ["AUTH_TOKEN"]
FROM_NUMBER = os.environ["FROM_NUMBER"] # e.g. whatsapp:+14100000000
TO_NUMBER = os.environ["TO_NUMBER"] # e.g. whatsapp:+61XXXXXXXXX
@app.post("/grafana-alert")
async def grafana_alert(req: Request):
body = await req.json()
# Grafana-style payload: status + alerts[]
status = body.get("status", "unknown")
alerts = body.get("alerts", [])
first = alerts[0] if alerts else {}
labels = first.get("labels", {})
annotations = first.get("annotations", {})
title = labels.get("alertname") or annotations.get("summary") or "Grafana alert"
severity = labels.get("severity", "n/a")
summary = annotations.get("description") or annotations.get("summary", "")
text = f"ALERT: {title}\nStatus: {status}\nSeverity: {severity}\n{summary}"
# Twilio WhatsApp API
url = f"https://api.twilio.com/2010-04-01/Accounts/{ACCOUNT_SID}/Messages.json"
data = {
"To": TO_NUMBER,
"From": FROM_NUMBER,
"Body": text,
}
resp = requests.post(url, data=data, auth=(ACCOUNT_SID, AUTH_TOKEN))
if resp.status_code >= 300:
raise HTTPException(status_code=500,
detail=f"Twilio error {resp.status_code}: {resp.text}")
return {"ok": True}
EOF
# ------ requirements.txt --------
cat > requirements.txt << 'EOF'
fastapi
uvicorn[standard]
requests
EOF
# --- create dockerfile in the same folder
cat > Dockerfile << 'EOF'
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8080
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]
EOF
# ----- replace $DOCKER_USER with your Docker Hub or other registry username and then build container
docker build -t $DOCKER_USER/grafana-whatsapp-bridge:v1 .
The bridge expects Grafana’s default webhook JSON, extracts a title, status, severity, and summary, (all configurable) builds a message string, and posts it to Twilio’s WhatsApp Messages API.
Run it bridge with your Twilio credentials as environment variables:
docker run --rm -p 8080:8080 \ -e ACCOUNT_SID=ACXXXXXXX \ -e AUTH_TOKEN=de0c290000000000 \ -e FROM_NUMBER='whatsapp:+141XXXXXXXX' \ -e TO_NUMBER='whatsapp:+61XXXXXXXXX' \ $DOCKER_USER/grafana-whatsapp-bridge:v1
Test it from a second terminal session:
curl -X POST http://localhost:8080/grafana-alert \ -H 'Content-Type: application/json' \ -d '{ "status": "firing", "alerts": [ { "labels": { "alertname": "TestAlert", "severity": "critical" }, "annotations": { "summary": "Test alert from curl" } } ] }'
If everything is configured correctly, you should receive a WhatsApp message containing the test alert details. At this point, you can adjust things like the message string, included labels, etc. I opted to push the image to my registry so that I can easily deploy it into my Kubernetes cluster to avoid having to troubleshooting networking issues; BUT this is a throwaway demo environment, and you'll need to consider the appropriate place for the bridge to be deployed in your environment and plan/secure accordingly.
docker push $DOCKER_USER/grafana-whatsapp-bridge:v1
Before we deploy the bridge into the cluster, we need to make sure it will be able to safely read the Twilio connection details. We can put all that info in a secret:
cat > twilio-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
name: twilio-whatsapp-secret
namespace: monitoring
type: Opaque
stringData:
ACCOUNT_SID: "ACXXXXXXX"
AUTH_TOKEN: "de0c290000000000"
FROM_NUMBER: "whatsapp:+141XXXXXXXX"
TO_NUMBER: "whatsapp:+61XXXXXXXXX"
EOF
# apply
kubectl apply -f twilio-secret.yaml
Now we can deploy the container into the same Kubernetes cluster as SAS Viya and Grafana. In my demo, I kept it simple by deploying it to the monitoring namespace, but that's not recommended for Production environments.
cat > whatsapp-bridge.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatsapp-bridge
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: whatsapp-bridge
template:
metadata:
labels:
app: whatsapp-bridge
spec:
containers:
- name: whatsapp-bridge
image: /grafana-whatsapp-bridge:v1
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: twilio-whatsapp-secret
---
apiVersion: v1
kind: Service
metadata:
name: whatsapp-bridge
namespace: monitoring
spec:
selector:
app: whatsapp-bridge
ports:
- port: 8080
targetPort: 8080
EOF
# apply
kubectl apply -f whatsapp-bridge.yaml
kubectl get pods -n monitoring -l app=whatsapp-bridge
When the pod is running, the service is reachable internally at:
http://whatsapp-bridge.monitoring.svc.cluster.local:8080/grafana-alert
Now, with the bridge up and running in the cluster, we can configure Grafana to connect to it via a webhook contact point. I've covered this in a previous post, but the high-level steps are:
In the Grafana UI, go to Alerting -> Contact points.
Click Create contact point.
Set:
Name: whatsapp-bridge
Type: Webhook
URL: http://whatsapp-bridge.monitoring.svc.cluster.local:8080/grafana-alert
Set the HTTP method to POST and keep the default JSON body.
Save the contact point.
You can test the contact point at this point to confirm that Grafana can reach the bridge and that WhatsApp notifications work. There's a 'Test' button on the editor page.
If everything is wired correctly, you should see a WhatsApp message appear on your phone a few seconds later.
Now, when alerts are created in Grafana, the contact point can be set to whatsapp-bridge in the 'Configure Notifications' pane:
Select any image to see a larger version. Mobile users: To view the images, select the "Full" version at the bottom of the page.
When an alert using that contact point triggers, a WhatsApp message arrives with details we've specified:
From here you can refine the message format, restrict WhatsApp alerts to only critical severities, or extend the bridge to send messages to multiple recipients or additional downstream systems. In real-world environments, some more planning and prep is required with particular consideration given to security, networking and to additional costs that may be incurred with this setup.
Find more articles from SAS Global Enablement and Learning here.
... View more