| title | Webhook ingress | |||||||
|---|---|---|---|---|---|---|---|---|
| category | integrations | |||||||
| tags |
|
|||||||
| summary | Push-based notifications from Sonarr / Radarr / Plex / Jellyfin so Auditarr reacts to file events in seconds, not poll-intervals. | |||||||
| help_context |
|
|||||||
| related |
|
By default, Auditarr polls each integration on its
configured poll_interval_seconds. Push-based notifications via
webhooks shorten the gap between an upstream file event (a new
download, a rename, a deletion) and Auditarr's view of the file —
seconds instead of poll-intervals.
This page walks through the per-service setup. The Auditarr side of the configuration is one button per integration; the upstream side is service-specific.
- Generate a per-integration webhook secret in Auditarr.
- Paste the secret + the webhook URL into the upstream service's webhook configuration.
- The upstream signs each webhook payload with HMAC-SHA256.
- Auditarr verifies the signature, reads the event type, and takes the corresponding action — reprobe (for adds / renames) or mark orphaned (for deletes).
- Open Settings → Integrations.
- Click the integration you want to wire up (must be Sonarr, Radarr, Plex, or Jellyfin).
- In the edit dialog, find the Webhook receiver section.
- Click Generate / rotate secret.
- Copy the secret immediately. It is displayed exactly once; the server stores only an encrypted hash. If you lose it, you can generate a new one — but any upstream using the old secret will start failing signature verification until reconfigured.
The webhook URL is shown in the same section. The format is:
https://<your-auditarr-host>/api/v1/webhooks/<kind>/<integration_id>
where <kind> is sonarr / radarr / plex / jellyfin and
<integration_id> is the integration's UUID.
Webhook payloads carry the upstream's view of file paths.
Auditarr's scanner sees its own local paths. Make sure the
integration's Path Mappings are configured before flipping
on webhooks — otherwise the dispatcher won't know how to
translate /data/media/movies/foo.mkv (Sonarr) into
/mnt/storage/Movies/foo.mkv (Auditarr).
- In Sonarr: Settings → Connect → + → Webhook.
- Name: anything descriptive (e.g.
Auditarr). - Triggers: enable
On File Import,On File Upgrade,On Rename,On Episode File Delete. LeaveOn Health Issueand the others off — Auditarr ignores those. - URL: paste the webhook URL from Auditarr.
- Method:
POST. - Username / Password: leave blank.
- Authentication / Header:
- Header name:
X-Auditarr-Signature - Header value format:
sha256={{TriggerName}}— Sonarr doesn't compute HMAC natively. Use the Sonarr Webhook connection's built-in signing if available, or run a proxy that signs the payload. Note: Sonarr's native Webhook connection does not yet support HMAC — Auditarr will 401 raw Sonarr webhooks. A companionsigned-webhookSonarr custom script that wraps the POST is the typical workaround. See the Caveats section below for details.
- Header name:
- Click Test in Sonarr. Auditarr should return 200 OK.
- Save.
Same as Sonarr (Radarr's webhook UI is identical). Use the
triggers On Movie Imported, On File Upgrade, On Rename,
On Movie File Delete.
- In Plex: Settings → Webhooks → Add Webhook (Plex Pass required).
- URL: paste the webhook URL from Auditarr.
- Plex does not support custom request headers natively
either. Use a proxy (e.g.
nginxwith a small Lua snippet, or a tiny FastAPI service) that:- Receives the raw Plex webhook,
- Computes HMAC-SHA256 over the body using your secret,
- Adds the
X-Auditarr-Signature: sha256=<hex>header, - Forwards to Auditarr.
- Save.
Auditarr only acts on the library.new event from Plex. Other
events (media.play, library.on.deck, etc.) are recognized
but discarded — they're handled by the existing playback poller.
- Install the Webhook plugin from Jellyfin's plugin catalog.
- Dashboard → Plugins → Webhook → Add Generic Destination.
- Webhook URL: paste the URL from Auditarr.
- Notification Type: enable
Item Added,Item Updated,Item Removed. Leave the rest off. - Headers (advanced section): add
X-Auditarr-Signature: sha256={{ HMAC the request body with the secret }}. The Jellyfin webhook plugin does not yet support HMAC computation natively either — same proxy workaround as Plex. - Save.
The big caveat in all four services above is that none of them natively compute HMAC over the request body. Industry practice for HMAC'd webhooks is to either:
- Run a small signing proxy in front of Auditarr (recommended for production), or
- Use a service like smee.io for tinkering, recognizing that the secret travels through the proxy.
The HMAC requirement is non-negotiable — without it, the webhook URL is an open POST endpoint that any internet host could hit. The 401 on a missing or invalid signature is what makes the endpoint safe to expose.
A future Stage may add per-service signing-helper scripts under
scripts/webhook-signers/. For now, see the
docs/integrations/sonarr.md and friends for service-specific
notes.
| Service | Event | Action |
|---|---|---|
| Sonarr | Download |
reprobe (re-read ffprobe metadata + hash) |
| Sonarr | Rename |
reprobe |
| Sonarr | EpisodeFileDelete |
mark the file orphaned in Auditarr |
| Radarr | Download |
reprobe |
| Radarr | Rename |
reprobe |
| Radarr | MovieFileDelete |
mark orphaned |
| Plex | library.new |
reprobe |
| Jellyfin | ItemAdded |
reprobe |
| Jellyfin | ItemUpdated |
reprobe |
| Jellyfin | ItemRemoved |
mark orphaned |
The Test event from every service returns 200 OK with no work
done — useful for verifying connectivity from the upstream's UI.
Unknown events return 200 OK with action: "ignored" in the
response body. This is intentional — non-200 responses cause most
upstreams to retry, and we don't want a retry storm if Sonarr
adds a new event type before Auditarr learns to recognize it.
When Auditarr reprobes a file in response to a webhook, it also:
- Computes the file's SHA-256 hash (chunked, async — no request-latency impact).
- If the VirusTotal integration is enabled (Settings → System → VirusTotal) and within daily quota, looks up the hash on VirusTotal's free-tier endpoint.
- Persists the result (counts of malicious / suspicious / harmless / undetected detections) next to the hash.
The Files page drawer's Security section surfaces both the hash and the VT result. Files unknown to VirusTotal are tagged "unknown"; files VT has seen show a quick clean/suspicious/malicious pill plus a click-through to the full VT report.
No file content is ever uploaded to VirusTotal — only the hash. The free tier supports hash-lookup only; file submission would require a paid tier and is explicitly out of scope.
-
All my webhook calls 401. The most common cause is that the upstream isn't signing the body. Verify with
curl:body='{"eventType":"Test"}' sig=$(printf '%s' "$body" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //') curl -X POST -H "X-Auditarr-Signature: sha256=$sig" \ -H "Content-Type: application/json" -d "$body" \ https://your-host/api/v1/webhooks/sonarr/<integration-id>
-
Reprobe runs but the file's data doesn't update. Check the integration's path mappings. A reprobe of an unknown path (after remapping) is a no-op — Auditarr won't fall through to scanning the filesystem from a webhook.
-
VirusTotal column stays blank. Confirm:
- The integration is enabled (Settings → System → VirusTotal).
- The API key secret is set under the VirusTotal API key slot.
- The daily quota (default 250) hasn't been exhausted — check
server logs for
virustotal.quota_exhausted. - The hash has been computed. Hashing happens on webhook reprobe events; legacy files stay unhashed until a fresh event arrives.