How to Receive Webhooks from Prometheus Alertmanager (Self-Hosted, Step by Step)

Receive Prometheus Alertmanager webhook notifications on your own server with Bearer token authentication, Docker setup, and RabbitMQ routing, verified end to end against a real Alertmanager 0.34.

By the end of this guide you will have a self-hosted endpoint that receives Prometheus Alertmanager webhook notifications, authenticates every delivery with a Bearer token, and routes the payloads to a log, RabbitMQ, or any other destination. Everything below was verified end to end against a real Alertmanager 0.34 instance and the real Core Webhook Module Docker image: Alertmanager's own notification pipeline sent the deliveries you see quoted here.

How Alertmanager webhooks work

Alertmanager takes alerts from Prometheus (or anything that POSTs to its API), groups them by your routing tree, and notifies receivers. The webhook_configs receiver POSTs a JSON document to your URL:

{
  "version": "4",
  "groupKey": "{}:{alertname=\"HighCPU\"}",
  "truncatedAlerts": 0,
  "status": "firing",
  "receiver": "cwm-gateway",
  "groupLabels": {},
  "commonLabels": {},
  "commonAnnotations": {},
  "externalURL": "http://alertmanager.example.com",
  "alerts": [
    {
      "status": "firing",
      "labels": {"alertname": "HighCPU", "instance": "web-01"},
      "annotations": {"summary": "CPU above 90% for 5 minutes"},
      "startsAt": "2026-08-29T10:15:00.000Z",
      "endsAt": "0001-01-01T00:00:00Z",
      "generatorURL": "http://prometheus.example.com/graph?...",
      "fingerprint": "c47eff52fe0ee323"
    }
  ]
}

status is firing or resolved (resolved notifications are on by default via send_resolved: true), the alerts array carries every alert in the group up to max_alerts (0 means all, with truncatedAlerts counting anything cut off), and the request arrives with User-Agent: Alertmanager/<version>.

Authentication: Alertmanager does not sign its webhooks. Instead, http_config on the webhook receiver supports an authorization header (type Bearer by default) or basic_auth. This guide uses the Bearer token, which the gateway compares in constant time.

Prerequisites

Step 1: Configure the receiver

Create a working directory with two files.

webhooks.json:

{
    "alertmanager_alerts": {
        "data_type": "json",
        "module": "log",
        "authorization": "Bearer {$ALERTMANAGER_WEBHOOK_TOKEN}"
    }
}

connections.json:

{}

What each parameter does:

  • alertmanager_alerts is the webhook ID; it becomes the URL path (/webhook/alertmanager_alerts).
  • module: log prints each authenticated payload to the container log. Zero dependencies for the first run; we swap in RabbitMQ at the end.
  • authorization requires the exact Authorization: Bearer <token> header on every request and compares it in constant time.
  • {$ALERTMANAGER_WEBHOOK_TOKEN} pulls the token from an environment variable, so it never lives in a config file.

Generate a strong token (or use our webhook secret generator):

openssl rand -hex 32

Step 2: Run it

docker run -d --name webhook-gateway -p 8000:8000 \
  -v "$PWD/webhooks.json:/app/webhooks.json:ro" \
  -v "$PWD/connections.json:/app/connections.json:ro" \
  -e ALERTMANAGER_WEBHOOK_TOKEN="your-generated-token" \
  spiderhash/webhook:latest

Confirm it is up:

docker logs webhook-gateway
# ... INFO: Application startup complete.

The receiver now answers on http://localhost:8000/webhook/alertmanager_alerts, with auto-generated API docs at http://localhost:8000/docs.

Step 3: Point Alertmanager at it

Add a receiver to your alertmanager.yml and route alerts to it:

route:
  receiver: cwm-gateway
  group_wait: 5s
  group_interval: 30s
  repeat_interval: 4h

receivers:
  - name: cwm-gateway
    webhook_configs:
      - url: https://your-domain/webhook/alertmanager_alerts
        send_resolved: true
        http_config:
          authorization:
            type: Bearer
            credentials: your-generated-token

Validate and reload:

amtool check-config alertmanager.yml
# then restart Alertmanager, or: curl -X POST http://localhost:9093/-/reload

The credentials value must match ALERTMANAGER_WEBHOOK_TOKEN exactly. If you prefer not to keep the token in alertmanager.yml, use credentials_file and mount the secret as a file.

Step 4: Test it

Fire a synthetic alert straight into Alertmanager's API; the notification pipeline treats it exactly like one from Prometheus:

curl -s -X POST http://localhost:9093/api/v2/alerts \
  -H "Content-Type: application/json" \
  -d '[{"labels":{"alertname":"HighCPU","instance":"web-01","severity":"critical"},"annotations":{"summary":"CPU above 90% for 5 minutes"}}]'

After group_wait (5 seconds in the config above) the delivery lands; docker logs webhook-gateway shows the payload arriving with user-agent: Alertmanager/0.34.0 and the response 200 OK.

You can also simulate the delivery directly against the gateway with the sample notification payload from the top of this article saved as notification.json:

curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:8000/webhook/alertmanager_alerts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-generated-token" \
  --data-binary @notification.json

Expected result:

{"message":"200 OK"}
HTTP 200

A wrong token:

{"detail":"Unauthorized"}
HTTP 401

And a request with no Authorization header at all:

{"detail":"Invalid Bearer token format: must start with 'Bearer '"}
HTTP 401

Those are the exact responses the gateway returns.

Troubleshooting

  • 401 {"detail":"Unauthorized"}: credentials in alertmanager.yml and ALERTMANAGER_WEBHOOK_TOKEN differ. They must be byte-identical; watch for trailing whitespace when using credentials_file.
  • 401 {"detail":"Invalid Bearer token format: must start with 'Bearer '"}: the Authorization header is missing or malformed. Check that http_config.authorization sits under the right webhook_configs entry (YAML indentation) and that no proxy strips the header.
  • Nothing arrives: run amtool check-config alertmanager.yml first; a config error means Alertmanager kept the old config. Then check the routing tree: the alert must match the route that points at your receiver. amtool config routes test --config.file=alertmanager.yml alertname=HighCPU shows which receiver a label set resolves to.
  • Duplicate or repeated notifications: that is grouping, not a bug. group_interval controls how often a changed group renotifies and repeat_interval how often an unchanged one repeats. Failed deliveries are retried by Alertmanager, so an endpoint that flaps can also produce repeats; acknowledge fast and hand off (next section).

Going to production

Printing to logs is not a pipeline. Swap the module for a real destination; here is the RabbitMQ variant, verified end to end (a real Alertmanager notification landed as a message in the alertmanager_alerts queue):

webhooks.json:

{
    "alertmanager_alerts": {
        "data_type": "json",
        "module": "rabbitmq",
        "connection": "rabbitmq_local",
        "module-config": {
            "queue_name": "alertmanager_alerts"
        },
        "authorization": "Bearer {$ALERTMANAGER_WEBHOOK_TOKEN}"
    }
}

connections.json:

{
    "rabbitmq_local": {
        "type": "rabbitmq",
        "host": "{$RABBITMQ_HOST:localhost}",
        "port": "{$RABBITMQ_PORT:5672}",
        "user": "{$RABBITMQ_USER:guest}",
        "pass": "{$RABBITMQ_PASS:guest}"
    }
}

The same pattern works for 17 other destinations: archive alert history to S3, publish to Kafka, or fan out to several at once with webhook chaining. Built-in rate limiting and retry handling cover the operational edges; an alert storm is exactly when you want the gateway acknowledging in milliseconds and queueing durably instead of making Alertmanager wait. The full authentication reference, including Bearer tokens, Basic auth, and 10 other methods, is in the docs.

Subscribe to Free Webhook Tool

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe