+
+## How It Works
+
+When a user adds something to their Plex watchlist, the sync engine normally routes it to Sonarr or Radarr. An exclusion is a per-user veto on that routing for a specific item — the sync engine sees the item, checks for an exclusion, and skips it if one exists.
+
+Common reasons to use this:
+
+- **You don't want a title auto-requested** even though a user has it watchlisted (e.g. content you've chosen not to host)
+- **Prevent re-request loops after Delete Sync** — when content is removed but stays on a user's watchlist, an exclusion stops the next sync from re-requesting it
+
+Exclusions clear automatically when the user removes the item from their Plex watchlist, so re-adding it later works normally.
+
+:::info Interaction with Delete Sync
+Excluded items are treated as unwatchlisted by Delete Sync. If you exclude something that's already in Sonarr/Radarr, the next Delete Sync run will remove it from your library.
+:::
+
+## Page Features
+
+The Watchlist Exclusions page shows all users' watchlist items in a sortable, filterable table:
+
+| Feature | Description |
+|---------|-------------|
+| **Search** | Filter items by title |
+| **User Filter** | Show items for specific users |
+| **Type Filter** | Filter by Movie or Show |
+| **Sorting** | Sort by title, status, or date added (default: newest first) |
+
+## Best Practices
+
+- Prefer excluding over asking users to remove items from their watchlists — the exclusion approach lets the item stay watchlisted (so they can still see it in Plex) without triggering a request
+- Exclusions are per-user. If you want to block something across everyone, you'll need to exclude it for each user
+
+## Troubleshooting
+
+| Problem | Solution |
+|---------|----------|
+| **Item still being requested** | Verify the exclusion exists for the correct user; check sync engine logs |
+| **Exclusion disappeared** | User likely removed the item from their Plex watchlist, which clears exclusions automatically |
+| **Item not showing in table** | Item may not be on any user's watchlist; check Plex watchlist status |
+
+## API Reference
+
+See the [Watchlist Exclusions API documentation](/docs/api/watchlist-exclusions) for detailed endpoint information.
diff --git a/docs/sidebars.ts b/docs/sidebars.ts
index f65b0a31b..f5263df3a 100644
--- a/docs/sidebars.ts
+++ b/docs/sidebars.ts
@@ -60,6 +60,7 @@ const sidebars: SidebarsConfig = {
'utilities/public-content-notifications',
'utilities/session-monitoring',
'utilities/user-tagging',
+ 'utilities/watchlist-exclusions',
],
},
{
diff --git a/docs/static/img/Watchlist-Exclusions.png b/docs/static/img/Watchlist-Exclusions.png
new file mode 100644
index 000000000..93ca36a70
Binary files /dev/null and b/docs/static/img/Watchlist-Exclusions.png differ
diff --git a/docs/static/openapi.json b/docs/static/openapi.json
index b0d921261..0d14ba21b 100644
--- a/docs/static/openapi.json
+++ b/docs/static/openapi.json
@@ -532,21 +532,25 @@
"properties": {
"days": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 365
},
"hours": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 8760
},
"minutes": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 525600
},
"seconds": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 31536000
},
@@ -813,21 +817,25 @@
"properties": {
"days": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 365
},
"hours": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 8760
},
"minutes": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 525600
},
"seconds": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 31536000
},
@@ -993,21 +1001,25 @@
"properties": {
"days": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 365
},
"hours": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 8760
},
"minutes": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 525600
},
"seconds": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 31536000
},
@@ -1301,21 +1313,25 @@
"properties": {
"days": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 365
},
"hours": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 8760
},
"minutes": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 525600
},
"seconds": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 31536000
},
@@ -2783,6 +2799,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -2917,11 +2934,13 @@
"properties": {
"sonarrSeriesId": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
"sonarrInstanceId": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -20252,6 +20271,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -20477,6 +20497,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -27773,6 +27794,7 @@
"plexPort": {
"default": 32400,
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -29516,6 +29538,7 @@
"properties": {
"instanceId": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -29544,6 +29567,7 @@
"properties": {
"id": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -29671,6 +29695,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -29695,6 +29720,7 @@
"properties": {
"id": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -30042,6 +30068,7 @@
"properties": {
"id": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
}
@@ -30481,6 +30508,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -30661,6 +30689,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -30949,6 +30978,7 @@
"schema": {
"default": 10,
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -31703,6 +31733,7 @@
"schema": {
"default": 10,
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -31817,6 +31848,7 @@
"schema": {
"default": 10,
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -31975,6 +32007,7 @@
"schema": {
"default": 10,
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -32133,6 +32166,7 @@
"schema": {
"default": 10,
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -33275,6 +33309,7 @@
"properties": {
"instanceId": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -33303,6 +33338,7 @@
"properties": {
"id": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -33430,6 +33466,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -33454,6 +33491,7 @@
"properties": {
"id": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -33597,6 +33635,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -33777,6 +33816,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
@@ -34252,6 +34292,7 @@
"properties": {
"id": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
}
@@ -34853,6 +34894,7 @@
{
"schema": {
"type": "integer",
+ "minimum": 0,
"exclusiveMinimum": true,
"maximum": 9007199254740991
},
diff --git a/migrations/migrations/091_20260516_add_watchlist_exclusions.ts b/migrations/migrations/091_20260516_add_watchlist_exclusions.ts
new file mode 100644
index 000000000..e06f05eed
--- /dev/null
+++ b/migrations/migrations/091_20260516_add_watchlist_exclusions.ts
@@ -0,0 +1,26 @@
+import type { Knex } from 'knex'
+
+export async function up(knex: Knex): Promise+ When enabled, vetoes routing of these keys for every current and + future user. Otherwise, excludes each item only for the user who + watchlisted it. +
+ > + )} + +Remove exclusion
++ per page +
+