# Integrations

> Send alerts to Alphorn from Grafana, Prometheus Alertmanager, Uptime Kuma, GitHub Actions, Jenkins, Home Assistant, Sonarr, and other tools via webhooks.

Source: https://docs.alphorn.dev/integrations/

Alphorn accepts standard HTTP webhooks, so it works with any tool that can send an HTTP request. This page shows how to connect popular services to Alphorn as notification sources.

For channel destinations (where Alphorn delivers notifications), see [Channels](/channels).

Alphorn picks the title and message out of common JSON fields automatically. For tools whose payload doesn't fit that, the **Create webhook** form has built-in [source presets](/webhook-templates#source-presets) (GitHub, Grafana, Prometheus Alertmanager, Sentry, Stripe) under **Advanced: field templates**, and you can write your own [field templates](/webhook-templates) for anything else.

## Monitoring

### Grafana

Grafana supports webhook contact points natively. Send alerts directly to Alphorn.

1. In Grafana, go to **Alerting → Contact points**
2. Click **Add contact point**
3. Select **Webhook** as the type
4. Set the URL to your Alphorn webhook URL
5. Set method to **POST**
6. Under **Optional Webhook settings**, set **Authorization Header - Scheme** to `Bearer` and **Authorization Header - Credentials** to your Alphorn API key

Grafana's default webhook payload has top-level `title` and `message` fields, which Alphorn picks up automatically, so no custom body is needed. To also tag notifications with the alert state, pick the **Grafana** [source preset](/webhook-templates#source-presets) when creating the webhook. It adds the tags `grafana,<state>` (for example `grafana,alerting` or `grafana,ok`).

### Prometheus Alertmanager

When creating the Alphorn webhook, pick the **Prometheus Alertmanager** [source preset](/webhook-templates#prometheus-alertmanager). Alertmanager's payload has no top-level title or message field, so without the preset the notification message is the raw JSON.

Then configure a webhook receiver in your Alertmanager config:

```yaml title="alertmanager.yml"
receivers:
  - name: alphorn
    webhook_configs:
      - url: "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT"
        send_resolved: true
        http_config:
          authorization:
            credentials: "alp_..." # your Alphorn API key
```

### Uptime Kuma

See the dedicated [Uptime Monitoring](/use-cases/uptime-monitoring) use case for setup instructions, including custom body templates for priority-based routing.

### Healthchecks.io

1. In your Healthchecks.io project, go to **Integrations**
2. Add a **Webhook** integration
3. Under **Execute when a check goes DOWN**, set the method to **POST** and the URL to your Alphorn webhook URL
4. If the webhook has **Require API key** switched on, add a request header `Authorization: Bearer alp_...` with your Alphorn API key
5. Set the request body to:

```json
{
  "title": "$NAME is $STATUS",
  "message": "Check $CODE: $STATUS. Tags: $TAGS",
  "priority": 5,
  "tags": ["healthchecks"]
}
```

6. Repeat for **Execute when a check goes UP** with the same method, URL, and header, but a lower priority (for example `"priority": 2`), so recoveries don't page anyone

### Gatus

[Gatus](https://github.com/TwiN/gatus) has native webhook alerting via its `custom` provider:

```yaml title="gatus-config.yaml"
alerting:
  custom:
    url: "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT"
    method: POST
    headers:
      Authorization: "Bearer ${ALPHORN_API_KEY}"
    body: |
      {
        "title": "[ENDPOINT_NAME] is [ALERT_TRIGGERED_OR_RESOLVED]",
        "message": "[ENDPOINT_GROUP] - [ENDPOINT_URL]",
        "priority": 4,
        "tags": ["gatus", "uptime"]
      }

endpoints:
  - name: website
    url: "https://example.com"
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: custom # without this, the custom provider never fires
        send-on-resolved: true
```

`[ALERT_TRIGGERED_OR_RESOLVED]` becomes `TRIGGERED` or `RESOLVED`, so the title tells you which one it is. The body is the same for both, so the priority can't change between them; it's set to `4` as a compromise. If you want resolved alerts to stay quiet, route on the title or drop `send-on-resolved`.

## CI/CD

### GitHub Actions

See the [CI/CD Pipelines](/use-cases/ci-cd-pipelines) use case for full GitHub Actions and GitLab CI examples.

### GitLab CI

See the [CI/CD Pipelines](/use-cases/ci-cd-pipelines#gitlab-ci) use case.

### Jenkins

Add a post-build step using `curl`. Store the API key as a **Secret text** credential (here with the ID `alphorn-api-key`) and set both variables in an `environment` block:

```groovy title="Jenkinsfile"
pipeline {
  agent any
  environment {
    ALPHORN_WEBHOOK = 'https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT'
    ALPHORN_API_KEY = credentials('alphorn-api-key')
  }
  stages {
    stage('Build') {
      steps { sh 'make build' }
    }
  }
  post {
    failure {
      sh '''
        curl -fsS -X POST "$ALPHORN_WEBHOOK" \
          -H "Authorization: Bearer $ALPHORN_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "title": "Jenkins build failed",
            "message": "'"$JOB_NAME"' #'"$BUILD_NUMBER"' - '"$BUILD_URL"'",
            "priority": 5,
            "tags": ["ci", "jenkins", "failure"]
          }'
      '''
    }
  }
}
```

## Infrastructure

### Docker / Watchtower

[Watchtower](https://github.com/containrrr/watchtower) can send webhook notifications when containers are updated:

```yaml title="docker-compose.yml"
services:
  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_NOTIFICATION_URL: "generic://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT?template=json&@Authorization=Bearer%20alp_..."
```

This is a [shoutrrr](https://github.com/containrrr/shoutrrr) generic webhook URL (`generic://` uses HTTPS by default). Don't use the `generic+https://` shortcut form here: it forwards all query parameters to the target, so `template` and `@` headers would have no effect. `template=json` sends a JSON body with `title` and `message`, and query parameters starting with `@` are sent as request headers, so `@Authorization=Bearer%20alp_...` carries your Alphorn API key (`%20` is the space).

### Ansible

Send notifications from Ansible playbooks using the `uri` module:

```yaml title="playbook.yml"
- name: Notify Alphorn
  uri:
    url: "{{ alphorn_webhook }}"
    method: POST
    headers:
      Authorization: "Bearer {{ alphorn_api_key }}"
    status_code: [200, 201] # Alphorn returns 201 Created
    body_format: json
    body:
      title: "Playbook completed: {{ ansible_play_name }}"
      message: "Deployed to {{ inventory_hostname }}"
      priority: 2
      tags:
        - ansible
        - deploy
```

### Terraform

Use a `null_resource` with a local-exec provisioner. The `timestamp()` trigger replaces the resource on every apply; without it, the provisioner runs only once, on first creation. `ALPHORN_WEBHOOK` and `ALPHORN_API_KEY` must be exported in the shell that runs `terraform apply`:

```hcl title="main.tf"
resource "null_resource" "notify" {
  depends_on = [aws_instance.web]

  triggers = {
    always_run = timestamp()
  }

  provisioner "local-exec" {
    command = <<EOT
      curl -fsS -X POST "$ALPHORN_WEBHOOK" \
        -H "Authorization: Bearer $ALPHORN_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{"title":"Infrastructure updated","message":"Terraform apply completed","priority":2,"tags":["terraform","infra"]}'
    EOT
  }
}
```

## Home Automation

### Home Assistant

Use the [RESTful notification](https://www.home-assistant.io/integrations/notify.rest/) integration:

```yaml title="configuration.yaml"
notify:
  - name: alphorn
    platform: rest
    resource: "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT"
    method: POST_JSON # send the body as JSON (plain POST sends form data)
    title_param_name: title
    message_param_name: message
    headers:
      Authorization: !secret alphorn_authorization # "Bearer alp_..." in secrets.yaml
    data:
      tags:
        - homeassistant
```

Home Assistant may send `data` values as strings, and Alphorn ignores a `priority` that isn't a number. To set one, use the webhook's **Priority template** (for example a fixed `3`) instead, or route by tags.

Then use it in automations:

```yaml
automation:
  - trigger:
      platform: state
      entity_id: binary_sensor.front_door
      to: "on"
    action:
      service: notify.alphorn
      data:
        title: "Front door opened"
        message: "Front door opened at {{ now().strftime('%H:%M') }}"
```

### Node-RED

Use an **HTTP Request** node:

1. Add an **HTTP Request** node
2. Set method to **POST**
3. Set URL to your Alphorn webhook URL
4. Connect a **Function** node before it to format the payload and set the headers:

```javascript
msg.payload = {
  title: "Node-RED Alert",
  message: msg.payload.toString(),
  priority: 3,
  tags: ["nodered"],
};
msg.headers = {
  "Content-Type": "application/json",
  Authorization: "Bearer " + env.get("ALPHORN_API_KEY"),
};
return msg;
```

## Media Servers

### Sonarr / Radarr / Lidarr

These tools support webhook notifications:

1. Go to **Settings → Connect**
2. Add a **Webhook** connection
3. Set the URL to your Alphorn webhook URL
4. Select which events to trigger on (grab, download, rename, etc.)

Their payload has no title or message field, so without templates the notification has no title and the message is the pretty-printed JSON (cut to 4000 characters). Add [field templates](/webhook-templates) when creating the webhook, for example Title `{eventType}: {series.title}` for Sonarr or `{eventType}: {movie.title}` for Radarr (check the field names against a real delivery).

If your version can't set a custom `Authorization` header, create this webhook with the API key switched off (**No API key (URL is the secret)**) and keep the URL private.

### Jellyseerr

Configure a webhook notification in the Jellyseerr settings pointing to your Alphorn webhook URL, and set **Authorization Header** to `Bearer alp_...` with your Alphorn API key.

## Programming Languages

Any HTTP client can send to Alphorn. See the [API Reference](/api-reference#examples) for examples in curl, JavaScript, Python, Go, PHP, and Ruby.

## Not seeing your tool?

Alphorn accepts any HTTP POST request. If a service supports webhooks, outgoing notifications, or custom HTTP integrations, it can send to Alphorn. Use the **Webhook** integration and point it at your Alphorn webhook URL. If the tool can send custom headers, add `Authorization: Bearer <your API key>`; if it can't, create the webhook with **No API key (URL is the secret)** and keep the URL private. Tools that can only send plain text work too: Alphorn uses the body as the message and reads the title, priority, and tags from the `X-Title`, `X-Priority`, and `X-Tags` headers (see [Plain Text](/api-reference#plain-text)).

If you've set up an integration not listed here, [open a PR](https://github.com/alphorn-dev/alphorn) to add it to this page.
