arr-media-stack/SKILL.md
Radarr, Sonarr, Prowlarr, Bazarr, and qBittorrent APIs for automated media management — search, add, monitor, and troubleshoot downloads
npx skillsauth add ddnetters/homelab-agent-skills arr-media-stackInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Manage your automated media stack: Radarr (movies), Sonarr (TV), Prowlarr (indexers), Bazarr (subtitles), and qBittorrent (downloads).
All APIs use X-Api-Key header for auth. Base URLs default to http://localhost:<port>/api/v3 (Radarr/Sonarr/Prowlarr) or http://localhost:<port>/api/v2 (Bazarr).
# Search by name
curl -s -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/movie/lookup?term=inception" | jq '.[0] | {title, year, tmdbId}'
# Add movie (use tmdbId from lookup)
curl -s -X POST -H "X-Api-Key: YOUR_RADARR_KEY" \
-H "Content-Type: application/json" \
-d '{
"tmdbId": 27205,
"title": "Inception",
"qualityProfileId": 1,
"rootFolderPath": "/movies",
"monitored": true,
"addOptions": {"searchForMovie": true}
}' "http://localhost:7878/api/v3/movie"
# List all movies
curl -s -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/movie" | jq '.[] | {id, title, year, monitored, hasFile}'
# Get specific movie
curl -s -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/movie/1"
# Delete movie (with files)
curl -s -X DELETE -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/movie/1?deleteFiles=true"
# Trigger search for missing movies
curl -s -X POST -H "X-Api-Key: YOUR_RADARR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "MissingMoviesSearch"}' \
"http://localhost:7878/api/v3/command"
# View download queue
curl -s -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/queue" | jq '.records[] | {title, status, sizeleft, timeleft}'
# System health
curl -s -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/health" | jq '.[] | {type, message}'
# Root folders
curl -s -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/rootfolder" | jq '.[] | {path, freeSpace}'
# Quality profiles
curl -s -H "X-Api-Key: YOUR_RADARR_KEY" \
"http://localhost:7878/api/v3/qualityprofile" | jq '.[] | {id, name}'
# Search by name
curl -s -H "X-Api-Key: YOUR_SONARR_KEY" \
"http://localhost:8989/api/v3/series/lookup?term=breaking+bad" | jq '.[0] | {title, year, tvdbId}'
# Add series
curl -s -X POST -H "X-Api-Key: YOUR_SONARR_KEY" \
-H "Content-Type: application/json" \
-d '{
"tvdbId": 81189,
"title": "Breaking Bad",
"qualityProfileId": 1,
"rootFolderPath": "/tv",
"monitored": true,
"addOptions": {"searchForMissingEpisodes": true}
}' "http://localhost:8989/api/v3/series"
# List all series
curl -s -H "X-Api-Key: YOUR_SONARR_KEY" \
"http://localhost:8989/api/v3/series" | jq '.[] | {id, title, year, monitored, episodeFileCount, episodeCount}'
# Upcoming episodes
curl -s -H "X-Api-Key: YOUR_SONARR_KEY" \
"http://localhost:8989/api/v3/calendar?start=$(date +%F)&end=$(date -d '+7 days' +%F)" | \
jq '.[] | {series: .series.title, episode: .title, airDate: .airDate}'
# Missing episodes
curl -s -H "X-Api-Key: YOUR_SONARR_KEY" \
"http://localhost:8989/api/v3/wanted/missing?pageSize=20" | jq '.records[] | {series: .series.title, episode: .title, airDate: .airDate}'
# Trigger search for missing episodes
curl -s -X POST -H "X-Api-Key: YOUR_SONARR_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "MissingEpisodeSearch"}' \
"http://localhost:8989/api/v3/command"
# Queue
curl -s -H "X-Api-Key: YOUR_SONARR_KEY" \
"http://localhost:8989/api/v3/queue" | jq '.records[] | {title, status, sizeleft, timeleft}'
# List indexers
curl -s -H "X-Api-Key: YOUR_PROWLARR_KEY" \
"http://localhost:9696/api/v1/indexer" | jq '.[] | {id, name, enable, protocol}'
# Test indexer
curl -s -X POST -H "X-Api-Key: YOUR_PROWLARR_KEY" \
-H "Content-Type: application/json" \
-d '{"id": 1}' \
"http://localhost:9696/api/v1/indexer/test"
# Sync indexers to Radarr/Sonarr
curl -s -X POST -H "X-Api-Key: YOUR_PROWLARR_KEY" \
"http://localhost:9696/api/v1/application/action/sync"
# List linked applications
curl -s -H "X-Api-Key: YOUR_PROWLARR_KEY" \
"http://localhost:9696/api/v1/application" | jq '.[] | {id, name, syncLevel}'
# Search across all indexers
curl -s -H "X-Api-Key: YOUR_PROWLARR_KEY" \
"http://localhost:9696/api/v1/search?query=inception&type=movie" | \
jq '.[] | {title, indexer, size, seeders}'
FlareSolverr runs alongside Prowlarr to bypass Cloudflare-protected indexers.
# Health check
curl -s http://localhost:8191/v1 -d '{"cmd": "sessions.list"}' -H "Content-Type: application/json"
Configure in Prowlarr: Settings → Indexer Proxies → Add → FlareSolverr → http://flaresolverr:8191
# List wanted subtitles (movies)
curl -s -H "X-Api-Key: YOUR_BAZARR_KEY" \
"http://localhost:6767/api/movies/wanted" | jq '.data[] | {title, missing_subtitles}'
# List wanted subtitles (series)
curl -s -H "X-Api-Key: YOUR_BAZARR_KEY" \
"http://localhost:6767/api/episodes/wanted" | jq '.data[] | {seriesTitle, episodeTitle, missing_subtitles}'
# Trigger search for wanted subtitles
curl -s -X POST -H "X-Api-Key: YOUR_BAZARR_KEY" \
"http://localhost:6767/api/subtitles/wanted/movies"
# System health
curl -s -H "X-Api-Key: YOUR_BAZARR_KEY" \
"http://localhost:6767/api/system/health"
qBittorrent uses cookie-based auth. Login first, then use the cookie.
# Login (save cookie)
curl -s -c /tmp/qbt-cookies.txt \
-d "username=admin&password=YOUR_PASSWORD" \
"http://localhost:8080/api/v2/auth/login"
# List all torrents
curl -s -b /tmp/qbt-cookies.txt \
"http://localhost:8080/api/v2/torrents/info" | \
jq '.[] | {name, state, progress, dlspeed, size}'
# List active downloads
curl -s -b /tmp/qbt-cookies.txt \
"http://localhost:8080/api/v2/torrents/info?filter=downloading" | \
jq '.[] | {name, progress: (.progress * 100 | round), dlspeed, eta}'
# Pause/resume torrent
curl -s -b /tmp/qbt-cookies.txt -d "hashes=TORRENT_HASH" \
"http://localhost:8080/api/v2/torrents/pause"
curl -s -b /tmp/qbt-cookies.txt -d "hashes=TORRENT_HASH" \
"http://localhost:8080/api/v2/torrents/resume"
# Delete torrent (with files)
curl -s -b /tmp/qbt-cookies.txt -d "hashes=TORRENT_HASH&deleteFiles=true" \
"http://localhost:8080/api/v2/torrents/delete"
# Transfer info (global speeds)
curl -s -b /tmp/qbt-cookies.txt \
"http://localhost:8080/api/v2/transfer/info" | jq '{dl_info_speed, up_info_speed}'
qBittorrent commonly runs through a VPN container (nordvpn, gluetun, etc.):
services:
vpn:
image: bubuntux/nordlynx # or qmcgaw/gluetun
cap_add: [NET_ADMIN]
ports:
- "8080:8080" # qBittorrent WebUI exposed through VPN
environment:
- PRIVATE_KEY=your-wireguard-key
qbittorrent:
image: lscr.io/linuxserver/qbittorrent
network_mode: "service:vpn" # Route all traffic through VPN
depends_on: [vpn]
# Search
curl -s -H "X-Api-Key: YOUR_OVERSEERR_KEY" \
"http://localhost:5055/api/v1/search?query=inception" | jq '.results[] | {title, mediaType, year}'
# Request a movie
curl -s -X POST -H "X-Api-Key: YOUR_OVERSEERR_KEY" \
-H "Content-Type: application/json" \
-d '{"mediaType": "movie", "mediaId": 27205}' \
"http://localhost:5055/api/v1/request"
# List pending requests
curl -s -H "X-Api-Key: YOUR_OVERSEERR_KEY" \
"http://localhost:5055/api/v1/request?filter=pending" | jq '.results[] | {id, type: .type, status: .status, media: .media.tmdbId}'
/opt/media/
├── downloads/ # qBittorrent download directory
├── movies/ # Radarr movie library
├── tv/ # Sonarr TV library
├── radarr/ # Radarr config
├── sonarr/ # Sonarr config
├── prowlarr/ # Prowlarr config
└── qbittorrent/ # qBittorrent config
| Problem | Service | Fix | |---------|---------|-----| | Download stuck at 100% | Radarr/Sonarr | Check import errors in Activity → Queue. Usually permissions or path mapping | | No results from search | Prowlarr | Check indexer health. Test individual indexers. Try FlareSolverr for Cloudflare sites | | "Path does not exist" | Radarr/Sonarr | Docker volume mounts don't match. Ensure paths inside container match config | | Torrent stalled | qBittorrent | Check VPN connection. Restart VPN container. Verify port forwarding | | Subtitle not found | Bazarr | Check provider API limits. Add more subtitle providers in Settings | | Can't reach qBittorrent UI | qBittorrent | It's behind VPN container. Check VPN container ports, not qBittorrent's | | Import failed | Radarr/Sonarr | Check PUID/PGID match between containers. Verify write permissions on library folder |
for svc in "radarr:7878" "sonarr:8989" "prowlarr:9696"; do
name="${svc%%:*}"; port="${svc##*:}"
status=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Api-Key: YOUR_KEY" \
"http://localhost:$port/api/v3/system/status")
echo "$name: $status"
done
development
Use when delegating a single coding task to `codex exec` ("hand off to codex", "run codex on this", "dispatch codex on this ticket", any one-shot invocation). Covers flags, sandbox traps, monitoring, and recovery. Not for multi-issue parallel batches — use codex-issue-waves for those.
development
Use when the user says "have codex fix this" / "have codex implement this" / "let codex handle this" / "give this to codex" / "delegate this to codex" for a single task with context already in scope (a Jira ticket, GitHub issue, file diff, bug, or described change). Plans the work, splits it into reviewable waves, dispatches codex per wave with review and correction between waves before opening a PR. Not for multi-issue parallel batches (use codex-issue-waves) or one-shot codex runs without planning (use invoking-codex-exec).
development
Run a batch of GitHub issues through codex exec in isolated git worktrees as parallel autonomous PRs, then manage the review and correction waves until merge. Use when the user gives a list of issue numbers (≥ 2) and asks to "spawn codex" / "dispatch codex" / "have codex work on" / "manage the PRs" / "process feedback" / "get them merged" for those issues, or when the user asks for multi-issue parallel delegation to codex. Not for single-issue wave-driven delegation (use codex-task-waves), single-issue one-shot dispatch (use invoking-codex-exec), or implementation without delegation (use /pr or direct implementation).
development
Slite knowledge base API — ask questions, search notes, retrieve content, manage users and groups, and audit knowledge health via the REST API