<!-- markdownlint-disable -->
<div align="center">

<a href="https://github.com/nicholas-fedor/shoutrrr">
    <img src="https://raw.githubusercontent.com/nicholas-fedor/shoutrrr/refs/heads/main/docs/assets/media/shoutrrr-logotype.svg" width="450" />
</a>

# Shoutrrr

A notification library for gophers and their furry friends.<br />
Heavily inspired by <a href="https://github.com/caronc/apprise">caronc/apprise</a>.

[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/nicholas-fedor/shoutrrr/badge)](https://scorecard.dev/viewer/?uri=github.com/nicholas-fedor/shoutrrr)
[![codecov](https://codecov.io/gh/nicholas-fedor/shoutrrr/branch/main/graph/badge.svg)](https://codecov.io/gh/nicholas-fedor/shoutrrr)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/47eed72de79448e2a6e297d770355544)](https://www.codacy.com/gh/nicholas-fedor/shoutrrr/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=nicholas-fedor/shoutrrr&amp;utm_campaign=Badge_Grade)
[![github code size in bytes](https://img.shields.io/github/languages/code-size/nicholas-fedor/shoutrrr.svg?style=flat-square)](https://github.com/nicholas-fedor/shoutrrr)
[![Pulls from DockerHub](https://img.shields.io/docker/pulls/nickfedor/shoutrrr.svg)](https://hub.docker.com/r/nickfedor/shoutrrr)
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/nicholas-fedor/shoutrrr)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/nicholas-fedor/shoutrrr)<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors-)<!-- ALL-CONTRIBUTORS-BADGE:END -->
[![license](https://img.shields.io/github/license/nicholas-fedor/shoutrrr.svg?style=flat-square)](https://github.com/nicholas-fedor/shoutrrr/blob/main/LICENSE)
</div>
<!-- markdownlint-restore -->

## Table of Contents

- [Full Documentation](#full-documentation)
- [Installation](#installation)
  - [From Source](#from-source)
  - [Binaries](#binaries)
  - [Container Images](#container-images)
  - [Go Package](#go-package)
  - [GitHub Action](#github-action)
- [Usage](#usage)
  - [CLI](#cli)
  - [Go Package Usage](#go-package-usage)
  - [Docker](#docker)
  - [GitHub Action Usage](#github-action-usage)
  - [Use as a Package](#use-as-a-package)
    - [Option 1 - Using the direct send command](#option-1---using-the-direct-send-command)
    - [Option 2 - Using a sender](#option-2---using-a-sender)
      - [Single URL](#single-url)
      - [Multiple URLs](#multiple-urls)
      - [Custom HTTP Client (SSRF / Egress Control)](#custom-http-client-ssrf--egress-control)
      - [Message Levels](#message-levels)
      - [Per-Target Errors](#per-target-errors)
      - [Context Propagation](#context-propagation)
      - [Format Conversion](#format-conversion)
  - [Use Through the CLI](#use-through-the-cli)
  - [Use as a GitHub Action](#use-as-a-github-action)
- [Supported Services](#supported-services)
  - [Service Discovery](#service-discovery)
- [Contributors ✨](#contributors-)
- [Related Project(s)](#related-projects)

## Full Documentation

Visit the project's [GitHub Page](https://shoutrrr.nickfedor.com) for full documentation.

## Installation

### From Source

```bash
go install github.com/nicholas-fedor/shoutrrr/shoutrrr@latest
```

### Binaries

Install the latest release binary to `$HOME/go/bin` (ensure it's in your `PATH`).

- **Windows (amd64):**

  ```powershell
  New-Item -ItemType Directory -Path $HOME\go\bin -Force | Out-Null; iwr (iwr https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | ConvertFrom-Json).assets.where({$_.name -like "*windows_amd64*.zip"}).browser_download_url -OutFile shoutrrr.zip; Add-Type -AssemblyName System.IO.Compression.FileSystem; ($z=[System.IO.Compression.ZipFile]::OpenRead("$PWD\shoutrrr.zip")).Entries | ? {$_.Name -eq 'shoutrrr.exe'} | % {[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$HOME\go\bin\$($_.Name)", $true)}; $z.Dispose(); rm shoutrrr.zip; if (Test-Path "$HOME\go\bin\shoutrrr.exe") { Write-Host "Successfully installed shoutrrr.exe to $HOME\go\bin" } else { Write-Host "Failed to install shoutrrr.exe" }
  ```

- **Linux (amd64):**

  ```bash
   mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*linux_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
  ```

- **macOS (amd64):**

  ```bash
   mkdir -p $HOME/go/bin && curl -L $(curl -s https://api.github.com/repos/nicholas-fedor/shoutrrr/releases/latest | grep -o 'https://[^"]*macOS_amd64[^"]*\.tar\.gz"' | grep -o 'https://[^"]*' | head -n1) | tar -xz -C $HOME/go/bin shoutrrr
  ```

> [!Note]
> Visit the [releases page](https://github.com/nicholas-fedor/shoutrrr/releases) for other architectures (e.g., arm, arm64, i386, riscv64).

### Container Images

- **[Docker Hub](https://hub.docker.com/r/nickfedor/shoutrrr):**

  ```bash
  docker pull nickfedor/shoutrrr:latest
  ```

- **[GHCR](https://github.com/users/nicholas-fedor/packages/container/package/shoutrrr):**

  ```bash
  docker pull ghcr.io/nicholas-fedor/shoutrrr:latest
  ```

> [!Note]
> Tags: `latest` (stable), `vX.Y.Z` (specific version), `nightly` (development), platform-specific (e.g., `amd64-nightly`).

### Go Package

```bash
go get github.com/nicholas-fedor/shoutrrr@latest
```

### GitHub Action

```yaml
- name: Shoutrrr
  uses: nicholas-fedor/shoutrrr-action@v1
  with:
    url: ${{ secrets.SHOUTRRR_URL }}
    title: Deployed ${{ github.sha }}
    message: See changes at ${{ github.event.compare }}.
```

## Usage

### CLI

```bash
shoutrrr send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"
```

### Go Package Usage

```go
import "github.com/nicholas-fedor/shoutrrr"

errs := shoutrrr.Send("slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook", "Hello, Slack!")
if len(errs) > 0 {
    // Handle errors
}
```

### Docker

```bash
docker run --rm nickfedor/shoutrrr:latest send --url "slack://hook:T00000000-B00000000-XXXXXXXXXXXXXXXXXXXXXXXX@webhook" --message "Hello, Slack!"
```

### GitHub Action Usage

See installation example [above](#github-action).

### Use as a Package

#### Option 1 - Using the direct send command

```go
url := "slack://token-a/token-b/token-c"
err := shoutrrr.Send(url, "Hello world (or slack channel) !")
```

#### Option 2 - Using a sender

##### Single URL

```go
url := "slack://token-a/token-b/token-c"
sender, err := shoutrrr.CreateSender(url)
params := types.Params{}
sender.Send("Hello world (or slack channel) !", &params)
```

##### Multiple URLs

```go
urls := []string {
  "slack://token-a/token-b/token-c"
  "discord://token@channel"
}
sender, err := shoutrrr.CreateSender(urls...)
params := types.Params{}
sender.Send("Hello world (or slack channel) !", &params)
```

##### Custom HTTP Client (SSRF / Egress Control)

```go
import (
    "crypto/tls"
    "log"
    "net/http"

    "github.com/nicholas-fedor/shoutrrr"
    "github.com/nicholas-fedor/shoutrrr/pkg/types"
)

customClient := &http.Client{
    Transport: &http.Transport{
        TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
    },
}

sender, err := shoutrrr.NewSenderWithOptions(
    nil,
    types.SenderOptions{HTTPClient: customClient},
    "discord://token@channel",
)
if err != nil {
    log.Fatal(err)
}
sender.Send("Hello with custom egress!", nil)
```

##### Message Levels

Services that support severity or priority can receive a semantic level through the `level` param.

Shoutrrr defines five levels: `Unknown`, `Debug`, `Info`, `Warning`, and `Error`. The default is `Info`.

```go
params := types.Params{}
params.SetLevel(types.Warning)
params.SetTitle("Disk usage high")

errs := sender.Send("Root partition at 92%", &params)
```

Services that implement `types.RichSender` (such as Discord) receive the full `[]types.MessageItem` slice when using `SendItems`, preserving level, fields, and file attachments.

Services that do not implement `RichSender` fall back to plain text automatically.

##### Per-Target Errors

`Sender.Send`, `*ServiceRouter.SendAsync`, and `*ServiceRouter.SendItems` return one error per unique configured target, in the deduplicated target order produced by `CreateSender`. Each error is wrapped in `*types.TargetError`, which carries the service URL/ID and supports `errors.Unwrap`, `errors.Is`, and `errors.As`:

```go
errs := sender.Send("deploy complete", nil)
for i, err := range errs {
    if err == nil {
        continue
    }
    var targetErr *types.TargetError
    if errors.As(err, &targetErr) {
        log.Printf("failed to send to %s: %v", targetErr.URL, targetErr.Err)
    }
}
```

##### Context Propagation

Services that implement `types.ContextSender` or `types.ContextAttachmentSender` receive a `context.Context` derived from the router's base context with a per-service timeout.

This enables cancellation and deadline propagation without changing the existing `Sender` or `RichSender` contracts.

##### Format Conversion

```go
import "github.com/nicholas-fedor/shoutrrr/pkg/format"

body, err := format.ConvertFormat("Hello **world**", "markdown", "text")
```

Supported conversions: `text` ↔ `markdown` ↔ `html`.

### Use Through the CLI

```bash
shoutrrr send [OPTIONS] <URL> <Message [...]>
```

### Use as a GitHub Action

You can also use Shoutrrr in a GitHub Actions workflow.

```yaml
name: Deploy
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - [Your other workflow steps]

      - name: Shoutrrr
        uses: nicholas-fedor/shoutrrr-action@v0.0.11
        with:
          url: ${{ secrets.SHOUTRRR_URL }}
          title: Deployed ${{ github.sha }}
          message: See changes at ${{ github.event.compare }}.
```

## Supported Services

| Service      | Description                          |
|--------------|--------------------------------------|
| Bark         | iOS push notifications               |
| Discord      | Discord webhooks                     |
| Generic      | Custom HTTP webhooks                 |
| Google Chat  | Google Chat webhooks                 |
| Gotify       | Gotify push notifications            |
| IFTTT        | IFTTT webhooks                       |
| Join         | Join push notifications              |
| Lark         | Lark (Feishu) webhooks               |
| Logger       | Local logging (for testing)          |
| Matrix       | Matrix rooms                         |
| Mattermost   | Mattermost webhooks                  |
| MQTT         | MQTT message broker                  |
| Notifiarr    | Notifiarr message forwarding         |
| Ntfy         | Ntfy push notifications              |
| Opsgenie     | Opsgenie alerts                      |
| PagerDuty    | PagerDuty incident notifications     |
| Pushbullet   | Pushbullet push notifications        |
| Pushover     | Pushover push notifications          |
| Rocket.Chat  | Rocket.Chat webhooks                 |
| Slack        | Slack webhooks or Bot API            |
| SMTP         | Email notifications                  |
| Teams        | Microsoft Teams webhooks             |
| Telegram     | Telegram bots                        |
| Twilio       | Twilio SMS notifications             |
| Zulip        | Zulip chat                           |
| XMPP         | XMPP messages (if enabled)           |

### Service Discovery

Use `services.SupportedSchemas()` and `services.SupportsSchema(schema)` to enumerate or check available notification services without constructing a router:

```go
import "github.com/nicholas-fedor/shoutrrr/pkg/services"

for _, schema := range services.SupportedSchemas() {
    fmt.Println(schema)
}

if services.SupportsSchema("discord") {
    // ...
}
```

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

<!-- markdownlint-disable -->
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
  <tbody>
    <tr>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/nicholas-fedor"><img src="https://avatars2.githubusercontent.com/u/71477161?v=4?s=100" width="100px;" alt="Nicholas Fedor"/><br /><sub><b>Nicholas Fedor</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=nicholas-fedor" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=nicholas-fedor" title="Documentation">📖</a> <a href="#maintenance-nicholas-fedor" title="Maintenance">🚧</a> <a href="https://github.com/nicholas-fedor/shoutrrr/pulls?q=is%3Apr+reviewed-by%3Anicholas-fedor" title="Reviewed Pull Requests">👀</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/amirschnell"><img src="https://avatars3.githubusercontent.com/u/9380508?v=4?s=100" width="100px;" alt="Amir Schnell"/><br /><sub><b>Amir Schnell</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=amirschnell" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://piksel.se"><img src="https://avatars2.githubusercontent.com/u/807383?v=4?s=100" width="100px;" alt="nils måsén"/><br /><sub><b>nils måsén</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=piksel" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=piksel" title="Documentation">📖</a> <a href="#maintenance-piksel" title="Maintenance">🚧</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/lukapeschke"><img src="https://avatars1.githubusercontent.com/u/17085536?v=4?s=100" width="100px;" alt="Luka Peschke"/><br /><sub><b>Luka Peschke</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=lukapeschke" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=lukapeschke" title="Documentation">📖</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/MrLuje"><img src="https://avatars0.githubusercontent.com/u/632075?v=4?s=100" width="100px;" alt="MrLuje"/><br /><sub><b>MrLuje</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=MrLuje" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=MrLuje" title="Documentation">📖</a></td>
      <td align="center" valign="top" width="14.28%"><a href="http://simme.dev"><img src="https://avatars0.githubusercontent.com/u/1596025?v=4?s=100" width="100px;" alt="Simon Aronsson"/><br /><sub><b>Simon Aronsson</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=simskij" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=simskij" title="Documentation">📖</a> <a href="#maintenance-simskij" title="Maintenance">🚧</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://arnested.dk"><img src="https://avatars2.githubusercontent.com/u/190005?v=4?s=100" width="100px;" alt="Arne Jørgensen"/><br /><sub><b>Arne Jørgensen</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=arnested" title="Documentation">📖</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=arnested" title="Code">💻</a></td>
    </tr>
    <tr>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/atighineanu"><img src="https://avatars1.githubusercontent.com/u/27206712?v=4?s=100" width="100px;" alt="Alexei Tighineanu"/><br /><sub><b>Alexei Tighineanu</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=atighineanu" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/ellisab"><img src="https://avatars2.githubusercontent.com/u/1402047?v=4?s=100" width="100px;" alt="Alexandru Bonini"/><br /><sub><b>Alexandru Bonini</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=ellisab" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://senan.xyz"><img src="https://avatars0.githubusercontent.com/u/6832539?v=4?s=100" width="100px;" alt="Senan Kelly"/><br /><sub><b>Senan Kelly</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=sentriz" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/JonasPf"><img src="https://avatars.githubusercontent.com/u/2216775?v=4?s=100" width="100px;" alt="JonasPf"/><br /><sub><b>JonasPf</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=JonasPf" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/claycooper"><img src="https://avatars.githubusercontent.com/u/3612906?v=4?s=100" width="100px;" alt="claycooper"/><br /><sub><b>claycooper</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=claycooper" title="Documentation">📖</a></td>
      <td align="center" valign="top" width="14.28%"><a href="http://ko-fi.com/disyer"><img src="https://avatars.githubusercontent.com/u/16326697?v=4?s=100" width="100px;" alt="Derzsi Dániel"/><br /><sub><b>Derzsi Dániel</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=darktohka" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://josephkav.io"><img src="https://avatars.githubusercontent.com/u/4267227?v=4?s=100" width="100px;" alt="Joseph Kavanagh"/><br /><sub><b>Joseph Kavanagh</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=JosephKav" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/issues?q=author%3AJosephKav" title="Bug reports">🐛</a></td>
    </tr>
    <tr>
      <td align="center" valign="top" width="14.28%"><a href="https://ring0.lol"><img src="https://avatars.githubusercontent.com/u/1893909?v=4?s=100" width="100px;" alt="Justin Steven"/><br /><sub><b>Justin Steven</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/issues?q=author%3Ajustinsteven" title="Bug reports">🐛</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/serverleader"><img src="https://avatars.githubusercontent.com/u/34089?v=4?s=100" width="100px;" alt="Carlos Savcic"/><br /><sub><b>Carlos Savcic</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=serverleader" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=serverleader" title="Documentation">📖</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/PythonGermany"><img src="https://avatars.githubusercontent.com/u/97847597?v=4?s=100" width="100px;" alt="PythonGermany"/><br /><sub><b>PythonGermany</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=PythonGermany" title="Code">💻</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://github.com/noelob"><img src="https://avatars.githubusercontent.com/u/643338?v=4?s=100" width="100px;" alt="Noel O'Brien"/><br /><sub><b>Noel O'Brien</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=noelob" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=noelob" title="Documentation">📖</a></td>
      <td align="center" valign="top" width="14.28%"><a href="https://caleblemoine.dev"><img src="https://avatars.githubusercontent.com/u/21261388?v=4?s=100" width="100px;" alt="Caleb Lemoine"/><br /><sub><b>Caleb Lemoine</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=circa10a" title="Code">💻</a> <a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=circa10a" title="Documentation">📖</a></td>
      <td align="center" valign="top" width="14.28%"><a href="http://hieddie.org"><img src="https://avatars.githubusercontent.com/u/205121303?v=4?s=100" width="100px;" alt="Eddie 'Sergeant' M"/><br /><sub><b>Eddie 'Sergeant' M</b></sub></a><br /><a href="https://github.com/nicholas-fedor/shoutrrr/commits?author=sgtM-devs" title="Documentation">📖</a></td>
    </tr>
  </tbody>
</table>

<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->
<!-- markdownlint-restore -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

## Related Project(s)

- [Watchtower](https://github.com/nicholas-fedor/watchtower) - Automate Docker container image updates.
- [Shoutrrr GitHub Action](https://github.com/nicholas-fedor/shoutrrr-action) - Notifications using Shoutrrr in GitHub Actions.
- [Bezel](https://github.com/henrygd/beszel) - A lightweight server monitoring platform that includes Docker statistics, historical data, and alert functions.
- [WatchYourLAN](https://github.com/aceberg/WatchYourLAN) - Lightweight network IP scanner with web GUI.
- [DNSControl](https://github.com/StackExchange/dnscontrol)- Infrastructure as code for DNS.
- [docker-volume-backup](https://github.com/offen/docker-volume-backup) - Backup Docker volumes locally or to any S3, WebDAV, Azure Blob Storage, Dropbox, Google Drive or SSH compatible storage.
- [BirdNET-Go](https://github.com/tphakala/birdnet-go) - An AI solution for continuous avian monitoring and identification.
