Skip to content

Notifications

vavallee edited this page Apr 15, 2026 · 1 revision

Notifications

Bindery fires a webhook when something interesting happens: a release is grabbed, a book is imported, a download fails, the service itself goes unhealthy, or a book upgrades to a preferred format. Notifications are generic HTTP webhooks — there are no built-in adapters for Slack/Discord/ntfy/etc. The page below shows how to wire each of those up to the generic webhook.

Events

Event Triggered when…
grabbed A release was sent to SABnzbd or qBittorrent
bookImported A downloaded file was successfully moved/renamed into the library
upgrade An already-imported book was replaced with a higher-ranked format
downloadFailed The download client reported a terminal failure
health A background health check flipped to unhealthy (indexer unreachable, disk full, etc.)

Each notification row has a flag per event (onGrab, onImport, onUpgrade, onFailure, onHealth), so one webhook endpoint can be subscribed to any subset.

Payload shape

All notifications POST a JSON body. The minimum shape is:

{ "eventType": "grabbed", "message": "…" }

Per-event payloads include the relevant book / release context. A grabbed event, for example, also includes the book title, author, release title, and indexer name. The easiest way to see the exact shape for your version is to create a notification pointing at a request-bin (https://webhook.site/) and fire the Test button in the UI.

The request always uses:

  • Content-Type: application/json
  • User-Agent: Bindery/1.0
  • HTTP method — configurable per notification (defaults to POST)
  • Any headers you add as the Headers JSON object (e.g. {"Authorization":"Bearer xyz"})

Responses in the 2xx range are treated as success. Anything else is logged as a failed webhook but does not retry — integrations that require at-least-once delivery should queue on their own end.

Recipes

ntfy

ntfy accepts the Bindery JSON body directly — just POST to your topic URL.

  • URL: https://ntfy.sh/your-topic (or self-hosted)
  • Method: POST
  • Headers: {"Title":"Bindery","Priority":"default","Tags":"books"}

For per-event titles, spin up one notification per event type so the Title header reflects the event.

Slack (incoming webhook)

Slack's incoming webhooks expect {"text": "..."}. Bindery sends a rich JSON body instead, so Slack will fall back to a best-effort render. For a cleaner message, send to an intermediary (like Huginn, n8n, or a one-line AWS Lambda) that reshapes the payload.

  • URL: your Slack app's incoming-webhook URL
  • Method: POST
  • Headers: {}

Discord

Discord's webhook API accepts {"content": "..."} or {"embeds": [...]}. Same caveat as Slack — Bindery's payload is not Discord-shaped, so either accept the raw-JSON fallback or run it through a reshaper.

  • URL: https://discord.com/api/webhooks/…/…
  • Method: POST

Home Assistant

Home Assistant's http integration exposes a webhook trigger that accepts any JSON — this is the cleanest target.

  • URL: https://homeassistant.local:8123/api/webhook/<webhook_id>
  • Method: POST
  • Headers: {}
  • Reference the payload inside your automation as {{ trigger.json.eventType }}, {{ trigger.json.message }}, etc.

Apprise

Apprise runs as a tiny HTTP service and speaks to ~100 providers (Telegram, Gotify, Mattermost, Pushover…). Bindery → Apprise → provider is the easiest way to get rich, per-provider formatting.

  • URL: http://apprise:8000/notify/<config_key>
  • Method: POST
  • Headers: {}

Apprise expects {"title":"…","body":"…","type":"info"}. Like Slack/Discord, you'll want a thin shim between Bindery and Apprise — or raise #95 if you'd like a built-in Apprise adapter.

SSRF guardrails

Webhook URLs are validated against internal/httpsec.ValidateOutboundURL with strict policy by default:

  • Loopback (127.0.0.0/8, ::1) — blocked
  • Link-local (169.254.0.0/16, fe80::/10) — blocked
  • Cloud-metadata (169.254.169.254, metadata.google.internal) — blocked
  • RFC1918 (10/8, 172.16/12, 192.168/16) — blocked

If your ntfy / Home Assistant / Apprise instance is on the same LAN, flip to LAN policy with:

BINDERY_NOTIFICATIONS_ALLOW_PRIVATE=1

The flag only loosens notifications — indexer and download-client URLs already default to LAN policy since homelabs almost always put Sonarr-adjacent services on RFC1918 space.

See Security for the full SSRF story.

Managing via API

# List
curl -H "X-Api-Key: $KEY" http://bindery:8787/api/v1/notification

# Create
curl -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -X POST http://bindery:8787/api/v1/notification \
  -d '{
    "name": "ntfy-books",
    "type": "webhook",
    "url": "https://ntfy.sh/my-books-topic",
    "method": "POST",
    "headers": "{\"Title\":\"Bindery\",\"Tags\":\"books\"}",
    "onGrab": true,
    "onImport": true,
    "onFailure": true,
    "onHealth": true,
    "onUpgrade": false,
    "enabled": true
  }'

See also

Clone this wiki locally