# CrossWatch(CW)

Self-hosted  sync hub with real-time scrobble (watcher/webhook) and much more.

<figure><img src="/files/qS075wnYoP0goVbu9cHH" alt=""><figcaption></figcaption></figure>

<a href="https://www.crosswatch.app" class="button primary" data-icon="globe">Website</a> <a href="https://github.com/cenodude/CrossWatch" class="button secondary" data-icon="github">GitHub</a> <a href="https://github.com/cenodude/CrossWatch/discussions" class="button secondary" data-icon="comments">Support</a>

Need to report something? [Bugs](https://github.com/cenodude/CrossWatch/issues) · [Feature requests](https://github.com/cenodude/CrossWatch/issues)

{% hint style="info" %}
**IMPORTANT:**\
\
If CW shows stale data, modals fail to open, or anything behaves unexpectedly, **refresh the page**

Also mark your CW URL as trusted in your browser. This prevents blocked redirects.
{% endhint %}

### Providers

<figure><img src="/files/6dH91lU9saf6kJ6cE2PX" alt=""><figcaption></figcaption></figure>

### Blueprint

<figure><img src="/files/qobK1mDtiKctJ6DICDNp" alt=""><figcaption></figcaption></figure>

### Quick start

* [Installation](/getting-started/installation)
* [First-time setup](/getting-started/first-time-setup)
* [Connect services](/crosswatch/settings/connections)

### Common tasks

* [Configure pairs](/crosswatch/settings/configure-pairs)
* [Syncing vs scrobbling](/crosswatch/settings/scrobbler)
* [Set up real-time scrobbling](/crosswatch/settings/scrobbler/watcher)
* [Debug sync issues](/crosswatch/main-dashboard/tools)


# About CrossWatch

Self-hosted watchlist, history, ratings, and resume progress sync plus real-time scrobbling

CW is a self-hosted **all-in-one tool** for sync, scrobbling, captures, and recovery.

It keeps **watchlists**, **history**, **ratings**, and **progress** aligned across your media servers and trackers.

Run it in Docker. Keep control of your data. Manage everything from one web UI.

{% hint style="success" %}
CW is easy to start and flexible enough to grow with your setup.
{% endhint %}

### What makes it flexible

* Use **profiles**, **pairs**, and **Watcher routes** for multi-account setups.
* Improve matching with **TMDb** and **Anime ID Mapping**.
* Recover more safely with local state and **captures**.

### Flexible real-time scrobbling

**Watcher** uses **routes mode**.

Each route maps one media server profile to one tracker profile.

That means you can:

* send playback from one server to multiple tracker targets
* split users or servers into separate routes
* run multiple media servers and trackers in one setup

### How it works

* **Providers** connect media servers and trackers.
* **Profiles** group multiple accounts or servers under one provider.
* **Pairs**, **runs**, and local **state** drive sync planning and scheduling.

### Start here

* [Installation](/getting-started/installation)
* [First-time setup](/getting-started/first-time-setup)
* [Configure Pairs](/crosswatch/settings/configure-pairs)


# Limitations

Soft size guidance, provider plan limits, and request-throttling limits that affect CrossWatch behavior.

CrossWatch is **not** built for very large libraries.

It is a UI-first tool with metadata enrichment, images, and safety checks. **It is not a bulk-sync engine.** Big libraries create heavy API traffic and large JSON files.&#x20;

{% hint style="warning" %}
If your main goal is syncing massive datasets fast, CW is the wrong fit.
{% endhint %}

### Size guidance

These are **approximate** limits. Provider behavior matters more than hardware.

On a normal machine or NAS, overall library size is rarely the first bottleneck.&#x20;

The tighter limits below are about API pressure and JSON file sizes

CW recommends these rough upper bounds for normal use:

* **Watchlist**: `~2,500`
* **History**: `~10,000`
* **Ratings**: `~10,000`

Lower counts are better. Higher counts may still work, but they are less tested and harder on providers.

### Large `state.json` files

CW stores sync state, history data, and provider mappings in `state.json`.

This file stays small during normal use. Large histories, watchlists, ratings, or provider datasets can grow it significantly.

Large files can slow the Web UI, API responses, and startup after a restart. They also increase memory use while CW reads or rewrites state.

Views that scan, filter, or summarize state feel this most. Multiple browser tabs can make the slowdown more noticeable.

As a rough guide, `75–100 MB+` may affect responsiveness on systems. Background syncs can still complete successfully.  Local state may have grown beyond the size CW currently optimizes for.

For large libraries:

* Sync history once then use watcher after you cleaned the state through Maintenance

### Provider account limits

Some limits come from the provider, not CW itself.

If you have large watchlist volumes, disable TMDb enrichment first.

### Provider request limits

CW also throttles upstream APIs to reduce `429 Too Many Requests` errors.

Default request limits:

* **SIMKL**: `10` GET/sec, `1` write/sec
* **Nuvio**: `100` GET/sec, `100` write/sec
* **MDBList**: `10` GET/sec, `1` write/sec
* **PublicMetaDB**: `20` GET/sec, `3` write/sec, plus hourly rating quotas
* **Trakt**: `3.33` GET/sec, `1` write/sec

Most installs should keep these defaults.

Only tune them if you see repeated `429`, temporary `5xx`, or timeout errors.

See [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries) for the short version and the full tuning reference.


# Installation

Choose an installation path for Docker or Unraid.

Choose the install path that fits your environment.

CrossWatch supports standard Docker setups and Unraid Community Apps.

### Install paths

{% columns %}
{% column %}

#### Installation

Use this for standard Docker, Docker Compose, Portainer, or NAS container UIs.

[View Installation](/getting-started/installation/docker-setup)
{% endcolumn %}

{% column %}

#### Unraid Apps

Use this if you install CrossWatch from the Unraid Community Apps catalog.

[View Unraid Apps](/getting-started/installation/unraid-apps)
{% endcolumn %}
{% endcolumns %}

### Quick guidance

Pick **Installation** if you want full control over volumes, ports, and runtime settings.

Pick **Unraid Apps** if you want the Unraid template to prefill the container basics.

After install, continue with [First-time setup](/getting-started/first-time-setup).


# Docker setup

Run CW in Docker and persist state by mounting /config.

Run CW in Docker. Always persist `/config`.

Expose `8787`.

Use either a Docker volume or a bind mount for `/config`.

Both options work fine.

If you use Portainer or a NAS container UI, use these same values.

{% hint style="info" %}
We do **not** provide support for installing or troubleshooting container runtimes.

These pages assume Docker (or your NAS container manager) is already installed and working.

If Docker itself is broken, use your platform docs first.
{% endhint %}

{% hint style="success" %}
Fastest path for most users:

* use a Docker volume
* keep port `8787`
* set only `TZ`
  {% endhint %}

{% columns %}
{% column %}

#### Docker volume

Best when you want the simplest setup.

Docker manages the storage path.
{% endcolumn %}

{% column %}

#### Bind mount

Best when you want a fixed host folder.

You manage host permissions yourself.
{% endcolumn %}
{% endcolumns %}

### Quick start

{% stepper %}
{% step %}

### Pick storage

Choose either a Docker volume or a bind mount for `/config`.

Volumes are simpler.
{% endstep %}

{% step %}

### Start the container

Use either the `docker run` or Docker Compose example below.

Expose `8787`.
{% endstep %}

{% step %}

### Open the UI

Open `http://localhost:8787`.

Continue with [First-time setup](/getting-started/first-time-setup).
{% endstep %}
{% endstepper %}

### Which should I use?

Both options are perfectly fine.

Pick the one that fits your setup.

Pick a Docker volume if you want:

* the simplest setup
* Docker to manage storage for you

Pick a bind mount if you want:

* files in a known host path
* a NAS share or fixed folder layout

{% hint style="info" %}
Quick rule:

* Use a Docker volume for simpler Docker-managed storage
* Use a bind mount for a specific host folder
  {% endhint %}

{% hint style="warning" %}
Bind mounts need correct host permissions.

CW must be able to write to the mounted directory.
{% endhint %}

### Before you run

* Pick one storage type for `/config`.
  * Docker volume: `crosswatch`
  * Bind mount: `/srv/crosswatch/config`
* Decide which port to expose.
  * Default UI port: `8787`
* Set your timezone.
  * Example: `TZ=Europe/Amsterdam`

{% hint style="warning" %}
Do not run without persistent storage on `/config`.

You will lose state when the container is recreated.
{% endhint %}

### Health check

CW exposes `GET /healthz`.

Expected response:

```json
{"ok":true,"status":"ok"}
```

It returns success when the app is ready.

The default examples below stay minimal.

If you want container health status, use the separate examples later on this page.

### Option A: Docker volume

Create the volume once:

```bash
docker volume create crosswatch
```

Run CW:

{% tabs %}
{% tab title="Docker" %}

```bash
docker run -d \
  --name crosswatch \
  -p 8787:8787 \
  -v crosswatch:/config \
  -e TZ=Europe/Amsterdam \
  --restart unless-stopped \
  ghcr.io/cenodude/crosswatch:latest
```

Open `http://localhost:8787`.
{% endtab %}

{% tab title="Docker Compose" %}

```yaml
services:
  crosswatch:
    image: ghcr.io/cenodude/crosswatch:latest
    container_name: crosswatch
    ports:
      - "8787:8787"
    environment:
      TZ: Europe/Amsterdam
    volumes:
      - type: volume
        source: crosswatch
        target: /config
    restart: unless-stopped

volumes:
  crosswatch:
```

{% endtab %}
{% endtabs %}

### Option B: Bind mount

Create a host directory first (below is just an example!):

```bash
mkdir -p /srv/crosswatch/config
```

{% hint style="warning" %}
Check permissions before you start.

CW must be able to read and write the host directory.

By default, the container runs as UID `1000` and GID `1000`.

If needed, either:

* change the host directory owner to `1000:1000`, or
* set `APP_UID` and `APP_GID` to match the host directory owner
  {% endhint %}

Run CW:

{% tabs %}
{% tab title="Docker" %}

```bash
docker run -d \
  --name crosswatch \
  -p 8787:8787 \
  -v /srv/crosswatch/config:/config \
  -e TZ=Europe/Amsterdam \
  --restart unless-stopped \
  ghcr.io/cenodude/crosswatch:latest
```

Open `http://localhost:8787`.
{% endtab %}

{% tab title="Docker Compose" %}

```yaml
services:
  crosswatch:
    image: ghcr.io/cenodude/crosswatch:latest
    container_name: crosswatch
    ports:
      - "8787:8787"
    environment:
      TZ: Europe/Amsterdam
    volumes:
      - type: bind
        source: /srv/crosswatch/config
        target: /config
    restart: unless-stopped
```

{% endtab %}
{% endtabs %}

### Container user and permissions

CW runs as a dedicated user inside the container.

It does not run the application as root.

Default container identity:

```
APP_USER=appuser
APP_GROUP=appuser
APP_UID=1000
APP_GID=1000
```

#### Docker managed volume

When `/config` uses a Docker managed volume, permission setup is usually not required.

Keep the defaults.

You can omit all four `APP_*` variables.

Example:

```yaml
volumes:
  - crosswatch:/config
```

CW creates the runtime user and group.

It prepares `/config` and applies the required ownership automatically.

#### Host directory bind mount

When `/config` maps to a host directory, the CW user must be able to read and write that directory.

Set `APP_UID` and `APP_GID` to match the host directory owner.

Leave `APP_USER` and `APP_GROUP` as `appuser` unless you need different names inside the container.

Example:

```yaml
services:
  crosswatch:
    image: ghcr.io/cenodude/crosswatch:latest
    environment:
      APP_UID: 1026
      APP_GID: 100
      APP_USER: appuser
      APP_GROUP: appuser
    volumes:
      - /volume1/docker/crosswatch:/config
```

In this example, CW runs inside the container as UID `1026` and GID `100`.

#### Find the correct UID and GID

Use one of these commands:

```bash
id -u
id -g
```

```bash
stat -c "%u:%g" /path/to/crosswatch
```

The first number is the UID.

The second number is the GID.

Example output:

```
1026:100
```

Use those values here:

```yaml
environment:
  APP_UID: 1026
  APP_GID: 100
```

#### Quick rules

* Docker managed volume:
  * keep the defaults
  * omit `APP_*` unless you have a specific reason
* Host directory bind mount:
  * set `APP_UID` and `APP_GID` to match the host directory owner
  * leave `APP_USER` and `APP_GROUP` as `appuser`

`APP_USER` and `APP_GROUP` are container-only names.

They do not need to match names on the Docker host.

#### Permission problems

When CW reports permission errors for `/config`, check the host directory ownership:

```bash
ls -ln /path/to/crosswatch
```

Then set `APP_UID` and `APP_GID` to the numeric owner and group shown there.

Or change the host directory ownership to match your configured values:

```bash
sudo chown -R 1000:1000 /path/to/crosswatch
```

Use the UID and GID that fit your environment.

{% hint style="warning" %}
Do not set the Docker Compose `user` property together with these variables.

CW needs startup permissions to prepare `/config`.

After that, it starts the application as the configured runtime user.
{% endhint %}

### Optional: Docker health check

Use this if you want Docker to mark the container as `healthy` or `unhealthy`.

It polls `http://127.0.0.1:8787/healthz`.

Defaults used below:

* interval: `30s`
* timeout: `5s`
* retries: `3`
* start period: `20s`

#### Docker volume + health check

{% tabs %}
{% tab title="Docker" %}

```bash
docker run -d \
  --name crosswatch \
  -p 8787:8787 \
  -v crosswatch:/config \
  -e TZ=Europe/Amsterdam \
  --health-cmd "python -c \"import json, urllib.request; data=json.load(urllib.request.urlopen('http://127.0.0.1:8787/healthz', timeout=5)); raise SystemExit(0 if data.get('ok') is True and data.get('status') == 'ok' else 1)\"" \
  --health-interval 30s \
  --health-timeout 5s \
  --health-retries 3 \
  --health-start-period 20s \
  --restart unless-stopped \
  ghcr.io/cenodude/crosswatch:latest
```

{% endtab %}

{% tab title="Docker Compose" %}

```yaml
services:
  crosswatch:
    image: ghcr.io/cenodude/crosswatch:latest
    container_name: crosswatch
    ports:
      - "8787:8787"
    environment:
      TZ: Europe/Amsterdam
    volumes:
      - type: volume
        source: crosswatch
        target: /config
    healthcheck:
      test:
        - CMD-SHELL
        - >-
          python -c "import json, urllib.request; data=json.load(urllib.request.urlopen('http://127.0.0.1:8787/healthz', timeout=5)); raise SystemExit(0 if data.get('ok') is True and data.get('status') == 'ok' else 1)"
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s
    restart: unless-stopped

volumes:
  crosswatch:
```

{% endtab %}
{% endtabs %}

#### Bind mount + health check

{% tabs %}
{% tab title="Docker" %}

```bash
docker run -d \
  --name crosswatch \
  -p 8787:8787 \
  -v /srv/crosswatch/config:/config \
  -e TZ=Europe/Amsterdam \
  --health-cmd "python -c \"import json, urllib.request; data=json.load(urllib.request.urlopen('http://127.0.0.1:8787/healthz', timeout=5)); raise SystemExit(0 if data.get('ok') is True and data.get('status') == 'ok' else 1)\"" \
  --health-interval 30s \
  --health-timeout 5s \
  --health-retries 3 \
  --health-start-period 20s \
  --restart unless-stopped \
  ghcr.io/cenodude/crosswatch:latest
```

{% endtab %}

{% tab title="Docker Compose" %}

```yaml
services:
  crosswatch:
    image: ghcr.io/cenodude/crosswatch:latest
    container_name: crosswatch
    ports:
      - "8787:8787"
    environment:
      TZ: Europe/Amsterdam
    volumes:
      - type: bind
        source: /srv/crosswatch/config
        target: /config
    healthcheck:
      test:
        - CMD-SHELL
        - >-
          python -c "import json, urllib.request; data=json.load(urllib.request.urlopen('http://127.0.0.1:8787/healthz', timeout=5)); raise SystemExit(0 if data.get('ok') is True and data.get('status') == 'ok' else 1)"
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s
    restart: unless-stopped
```

{% endtab %}
{% endtabs %}

### Optional environment variables

You normally only need `TZ`.

Override these only if you know why:

* `TZ` — container timezone (used for timestamps in logs and the UI).
* `CONFIG_BASE` — base path for `config.json`, `state.json`, `statistics.json`, etc.
  * Default: `/config` (leave as-is)
* `WEB_HOST` — bind address for the web UI inside the container.
  * Default: `0.0.0.0`
* `WEB_PORT` — port for the web UI inside the container.
  * Default: `8787`
* `APP_UID` — numeric UID for the runtime user inside the container.
  * Default: `1000`
* `APP_GID` — numeric GID for the runtime group inside the container.
  * Default: `1000`
* `APP_USER` — runtime username inside the container.
  * Default: `appuser`
* `APP_GROUP` — runtime group name inside the container.
  * Default: `appuser`
* `APP_DIR` — application directory inside the container.
  * Default: `/app` (leave as-is)
* `RUNTIME_DIR` — runtime/config directory used by the entrypoint.
  * Default: `/config` (leave as-is)
* `CW_RESET_AUTH_ONCE` — one-time UI auth recovery flag.
  * Set to `1` to clear stored auth and sessions on next start.
  * Remove it after the reset completes.
* `RELOAD` — enable Python auto-reload for development.
  * Default: `no` (set to `yes` for dev only)

{% hint style="info" %}
Use an IANA timezone name (like `Europe/Amsterdam`), not a GMT offset.
{% endhint %}

### Troubleshooting

#### UI does not load

* Confirm the container is running: `docker ps`
* Check logs: `docker logs crosswatch --tail 200`
* Confirm the port mapping matches the URL you open.
  * Example: `-p 8787:8787` -> `http://localhost:8787`

#### `/config` is not writable

This happens most often with bind mounts.

* Check the host directory exists.
* Check the container user can write to it.
* Align the host directory owner with `APP_UID` and `APP_GID`.
* On NAS platforms, also verify the shared folder ACLs.

### Next steps

* [First-time setup](/getting-started/first-time-setup) - connect providers and create your first pair
* [Best practices](/getting-started/best-practices) - safer defaults for your first pairs
* [Settings](/crosswatch/settings) - connect providers and configure pairs


# Unraid Apps

Install CW from the Unraid Community Apps catalog.

CW is available in the Unraid Apps store.

Install and configure it from the **Apps** tab.

<figure><img src="https://cdn.simpleicons.org/unraid/F15A2C" alt="Unraid logo" width="72"><figcaption></figcaption></figure>

### What the template includes

The Unraid template includes:

* persistent `appdata` storage
* WebUI configuration
* timezone support
* native Unraid `UID` and `GID` defaults

### Install from Community Apps

Use the official catalog entry:

[Open CrossWatch in Community Apps](https://ca.unraid.net/apps/crosswatch-1w9f6bx1i195yh?q=crosswatch)

### Notes

The Unraid template handles the core container settings for you.

You still need to complete the app setup in CW after the container starts.

### Next steps

* [First-time setup](/getting-started/first-time-setup) - connect providers and create your first pair
* [Best practices](/getting-started/best-practices) - safer defaults for your first pairs
* [Installation](/getting-started/installation) - use the manual container path instead of the Unraid template


# First-time setup

Connect providers and add TMDb metadata

Use this after CrossWatch (CW) is running and the UI loads.

If you still need to install it, start here: [Docker setup](/getting-started/installation).

Need a platform-specific wrapper instead? See [Install scripts](/related-information/community/install-scripts) for community-maintained options.

{% hint style="success" %}
CW can do a lot. You do **not** need to configure everything.

For most users, the **defaults are already correct**. Your first goal is a single clean run.

Typical first setup:

* Connect one **media server or media client** and one **tracker**
* Add the Metadata provider (**TMDb**)
* Create one **pair** (one-way and one feature)
  {% endhint %}

{% hint style="warning" %}
**Save often.** Most changes do nothing until you click the **red floating Save button**.\
Click **Save** after every step below.<br>

If your watchlists, history, or ratings are far beyond the recommended ranges, stop here and read [Limitations](/getting-started/limitations).
{% endhint %}

<table data-view="cards"><thead><tr><th>Title</th><th data-card-target data-type="content-ref">Target</th></tr></thead><tbody><tr><td>Connect providers first</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/lCTegLtxemF834DhMv5R">/spaces/3rh5THg1PdhVsBt3GALo/pages/lCTegLtxemF834DhMv5R</a></td></tr><tr><td>Configure sync pairs</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/C1lNlN7T3a0xWpl0jEU5">/spaces/3rh5THg1PdhVsBt3GALo/pages/C1lNlN7T3a0xWpl0jEU5</a></td></tr><tr><td>Enable real-time playback</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/J9gNWTn8GfZMQxOd5AW2">/spaces/3rh5THg1PdhVsBt3GALo/pages/J9gNWTn8GfZMQxOd5AW2</a></td></tr><tr><td>Check exact provider support</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/xWABFdLxQZxYgpBKwn8o">/spaces/3rh5THg1PdhVsBt3GALo/pages/xWABFdLxQZxYgpBKwn8o</a></td></tr></tbody></table>

If you only need real-time playback, jump to [Webhook or Watcher](/crosswatch/settings/scrobbler/webhook-or-watcher).

### Recommended order

1. Connections
2. Metadata / ID Mapping (TMDb)
3. Synchronization (optional)
4. Scrobbler (optional)

{% stepper %}
{% step %}

### 1) Connect providers

Open **Settings → Connections**.

Start with **one media server or media client** and **one tracker**. Validate one clean run. Then add more providers.

Good first trackers are **Trakt**, **SIMKL**, **MDBList**, or **PublicMetaDB**.

1. Click **Connect** / **Sign In** for the provider.
2. Approve access in the new tab.
3. Return to CW.
4. Verify URLs and IDs. Use **Auto-Fetch** when available.
5. Click the **red Save button**.

Related:

* [Connections](/crosswatch/settings/connections)
  {% endstep %}

{% step %}

### 2) Configure metadata (TMDb)

Open **Settings → Connections**.

Set up TMDb before your first real sync. It improves matching and reduces wrong links.

TMDb is strongly recommended.

CrossWatch uses TMDb for many features, including posters, backdrops, richer metadata, and more reliable matching.

1. Enable **TMDb**.
2. Create a TMDb API key.
3. Paste the key into CW.
4. Click the **red Save button**.

Related: [Meta: TMDb](/crosswatch/settings/connections/metadata/tmdb-metadata)
{% endstep %}

{% step %}

### 3) Create your first pair

If you want syncing, open **Synchronization**.

{% hint style="info" %}
Do not overthink this screen. The defaults are good for most users.

You can leave **Global** and **Provider** settings unchanged.
{% endhint %}

Use the safest first run:

* **Mode**: one-way
* **Features**: enable **one** (Watchlist, History, Ratings, or Progress)
* **Dry run**: enabled

{% hint style="info" %}
Progress shows up for **Plex**, **Emby**, **Jellyfin**, **Nuvio**, and **PublicMetaDB** pairs.
{% endhint %}

1. Pick **Source** (left) and **Target** (right).
2. Enable one feature tab.
3. Keep defaults in **Global** and **Provider** settings.
4. Click the **red Save button**.

Related:

* [Configure Pairs](/crosswatch/settings/configure-pairs)
  {% endstep %}

{% step %}

### 4) Run once, then iterate

Run a sync. Review what it planned. Fix matching before you enable more features.

Use these tools when something looks off:

* [Analyzer](/crosswatch/main-dashboard/tools/analyzer) for missing peers and mismatches
* [Editor](/crosswatch/main-dashboard/navigation/editor) for overrides, blocks, and cleanup

When the pair is stable:

* Disable **Dry run**.
* Add one more feature.
* Add the next pair.
  {% endstep %}

{% step %}

### 5) Optional: enable scrobbling (Watcher)

Open **Settings → Scrobbler**.

Use **Watcher** by default. Use Webhooks when server-delivered events suit your setup.

{% hint style="info" %}
Create one route, then start Watcher or enable Autostart.
{% endhint %}

1. Connect your media server and at least one tracker target.
2. Open **Settings → Scrobbler**.
3. Select **Add watcher**.
4. Select the source and destination profiles.
5. Select **Save changes**.
6. Start Watcher or enable **Autostart**.

{% hint style="info" %}
Most setups should use only one mode, preferable Watcher.

You can run **Watcher** and **Webhooks** together when they own separate source and destination pairs.
{% endhint %}

Related:

* [Webhook or Watcher](/crosswatch/settings/scrobbler/webhook-or-watcher)
* [Watcher](/crosswatch/settings/scrobbler/watcher)
  {% endstep %}

{% step %}

### 6) Optional: add scheduling

If you want automatic periodic runs, open **Settings → Scheduling**.

Start with a daily cadence. Avoid overlaps.

Click the **red Save button**.

Related: [Scheduling](/crosswatch/settings/scheduling)
{% endstep %}
{% endstepper %}

### Quick checklist

* Providers are connected.
* Providers show up in **Sync pairs**.
* TMDb is configured and saved.
* First pair is one-way and dry run.
* Scrobbling is configured only if you need it.
* You clicked the red **Save** button after each step.

Related: [Settings](/crosswatch/settings)

### Summary

Connect one media server and one tracker.

Add TMDb before your first real run.

### Next steps

* Lock in safe pair defaults: [Best practices](/getting-started/best-practices)
* Check whether your library size is a good fit: [Limitations](/getting-started/limitations)
* Deep-dive pair controls (guards, features, Blackbox): [Configure Pairs](/crosswatch/settings/configure-pairs)
* Enable real-time plays once syncing is stable: [Watcher](/crosswatch/settings/scrobbler/watcher)
* Debug missing peers and ID issues: [Analyzer](/crosswatch/main-dashboard/tools/analyzer)


# Best practices

Recommended default

Use these defaults unless you have a reason to deviate.

CrossWatch (CW) is safest when you start small and keep runs predictable.

### Default setup (recommended)

* Direction: **Media server -> tracker**
* Watchlist: start **one-way**. Can be one-way or two-way.
* History: do a **one-way seed** once. Then disable History and use scrobbling.
* Ratings: keep **one-way** (Media server -> tracker)
* Progress: keep **one-way** (Media server -> tracker)

{% hint style="warning" %}
Do not switch to **tracker -> media server** unless you have a clear reason.

Trackers often(always) contain items your server does not have..

For most setups, **only sync data into a media server when the item already exists in that library.**

Related: [Trackers vs. Media Servers](/related-information/trackers-vs.-media-servers) and [Tracker to Media Server](/related-information/tracker-to-media-server)
{% endhint %}

CW supports **real bidirectional synchronization**.  Changes on either platform can sync in both directions. That adds flexibility. It also adds risk. Because both sides can introduce changes, you are more likely to see:

* conflicts, duplicate items, unintended overwrites, propagated deletions

**Use bidirectional (two-way) sync carefully.**

It may look like the better option. For most users, **one-way** sync is still the safer and more predictable

### Metadata agents (recommended)

Good metadata is the difference between stable ID matching and messy title guessing.

Use these defaults:

* **Plex Media Server**: use the default **Plex Movie** and **Plex TV Series** metadata agents.
* **Emby** and **Jellyfin**: use **TMDb** metadata.

For AniList pairs specific pairs, enable **Anime ID Mapping**.

Guide: [Anime ID Mapping](/crosswatch/settings/connections/metadata/anime-id-mapping)

### Changing pairs and resets

Do not constantly tweak direction, mode, or enabled features. If you do, reset state so planning recalculates cleanly.

#### When to rebuild sync state and retry provider items

Run **Rebuild sync state** and **Retry provider items** when you make a major sync change or the next plan looks unsafe.

Common triggers:

* You switch **one-way ↔ two-way**
* You change library filters or whitelists on an active pair
* You update CW and the first plan looks wrong
* You backfill history with old dates and the provider uses delta reads
* You see an unexpected large remove plan


# Support matrix

Provider and feature compatibility across syncing, scrobbling, playback, and tooling.

### Status legend

| Status                          | Meaning                                                         |
| ------------------------------- | --------------------------------------------------------------- |
| <i class="fa-check">:check:</i> | Implemented and exposed by the current code                     |
| Limited                         | Implemented with provider, media type, or workflow restrictions |
| Source only                     | Can be read as a source, but not used as a writable destination |
| <i class="fa-xmark">:xmark:</i> | Not implemented for this function                               |
| N/A                             | Not applicable                                                  |

### Quick answers

{% hint style="success" %}
Best-supported first path:

* media server → tracker for watchlist, history, or ratings

* one-way sync

* TMDb enabled
  {% endhint %}

* Normal pair-based `progress` sync is available for CrossWatch local, Plex, Jellyfin, Emby, Nuvio, Floppy, PublicMetaDB, PunchPlay, Scrob, Trakt, SIMKL, and MDBList.

* Watcher supports Scrob as a source or sink. Webhooks support Scrob as a sink only.

* Jellyfin and Emby do not have a native watchlist. CrossWatch maps watchlists to Favorites, Playlists, or Collections.

* Tautulli is a read-only history source. It cannot receive writes.

* Nuvio is an experimental media client. It supports watchlist, history, and progress.

* PunchPlay is an experimental tracker. It supports watchlist, ratings, history, progress, and Watcher.

* Scrob is experimental. It supports watchlist, ratings, history, rewatches, progress, and real-time scrobbling.

* TMDb supports watchlist and ratings sync only.

* AniList supports anime watchlist and ratings workflows only.


# Providers

Provider capabilities, entity support, watchlist mapping, and authentication methods.

This section shows provider support for sync, watchlists, ratings, history, progress, and setup.

{% hint style="success" %}
Best first pair:

* source: Plex, Jellyfin, or Emby
* target: Trakt, SIMKL, MDBList, or PublicMetaDB
* mode: one-way
  {% endhint %}

{% hint style="warning" %}
Jellyfin and Emby do not have a native watchlist.

CrossWatch maps watchlists to Favorites, Playlists, or Collections.
{% endhint %}

{% hint style="info" %}
Tautulli is read-only.

Use it only as a history source.
{% endhint %}

### Use this page for quick checks

* Which providers can act as sources or targets
* Which features each provider supports
* Which entity types each feature can handle
* Which auth and connection methods each provider uses

### Provider overview

#### Provider roles

| Provider         | Category        |                                          Pair source |                                          Pair target |                                       Named profiles |
| ---------------- | --------------- | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: |
| Emby             | Media server    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Jellyfin         | Media server    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Plex             | Media server    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Kodi             | Media client    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Nuvio            | Media client    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Stremio          | Media client    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| AniList          | Anime tracker   | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Floppy           | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| MDBList          | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| PublicMetaDB     | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| PunchPlay        | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Scrob            | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| SIMKL            | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| TMDb             | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Trakt            | Tracker         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| CrossWatch local | Local tracker   | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |                                          Local state |
| Tautulli         | Plex monitoring |                                          Source only |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |

#### Sync feature support

| Provider         |                                       Watchlist sync |                                         Ratings sync |                                         History sync |                                        Progress sync |
| ---------------- | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: |
| CrossWatch local | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Plex             | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Jellyfin         |                                              Limited |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Emby             |                                              Limited |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Floppy           | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Nuvio            | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Kodi             |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Stremio          | <i class="fa-check" style="color:green;">:check:</i> |                                          Target only | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Trakt            | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| SIMKL            | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| MDBList          | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| PublicMetaDB     | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| PunchPlay        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Scrob            | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| AniList          |                                              Limited |                                              Limited |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| TMDb             | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Tautulli         |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |                                          Source only |   <i class="fa-xmark" style="color:red;">:xmark:</i> |

#### Real-time and manager support

| Provider         |                                     Progress Manager |                                       Watcher source |                                       Webhook source |                                         Watcher sink |                                         Webhook sink |
| ---------------- | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: |
| CrossWatch local |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Plex             | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Jellyfin         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Emby             | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Floppy           |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Nuvio            |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Kodi             | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Stremio          |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Trakt            | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| SIMKL            | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| MDBList          | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| PublicMetaDB     | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| PunchPlay        |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Scrob            |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| AniList          |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| TMDb             |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Tautulli         |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |

#### Notes

* Jellyfin and Emby have no native watchlist.
* Jellyfin and Emby Playlist mode is episode-only.
* Jellyfin and Emby ratings sync is disabled.
* Floppy is experimental. It supports watchlist, ratings, and history only.
* Nuvio is experimental. It supports watchlist, history, and progress only.
* PunchPlay is experimental. It supports watchlist, ratings, history, progress, and Watcher.
* Scrob is experimental. It supports sync, rewatches, and Watcher routes.
* Kodi is experimental. It supports ratings, history, progress, and Watcher only.
* Stremio is experimental. Ratings can sync to Stremio only.
* AniList supports anime watchlist and ratings only.
* Tautulli is read-only and history-only.
* Playlist sync is not a CrossWatch feature.
* Trakt, SIMKL, and MDBList support Progress Manager, not normal `progress` sync.
* PublicMetaDB supports both normal `progress` sync and Progress Manager.

### Provider entity support

#### Entity coverage

| Provider         | Watchlist entities               | Rating entities                  | History entities                      | Progress entities                |
| ---------------- | -------------------------------- | -------------------------------- | ------------------------------------- | -------------------------------- |
| CrossWatch local | Movies, shows, seasons, episodes | Movies, shows, seasons, episodes | Movies, shows, seasons, episodes      | Movies, shows, seasons, episodes |
| Plex             | Movies, shows                    | Movies, shows, seasons, episodes | Movies, episodes                      | Movies, episodes                 |
| Jellyfin         | Movies, shows, library items     | Not supported                    | Movies, episodes                      | Movies, episodes                 |
| Emby             | Movies, shows, library items     | Not supported                    | Movies, episodes                      | Movies, episodes                 |
| Floppy           | Movies, shows                    | Movies, shows                    | Movies, episodes                      | Not supported                    |
| Nuvio            | Movies, shows                    | Not supported                    | Movies, episodes                      | Movies, episodes                 |
| Kodi             | Not supported                    | Movies, episodes                 | Movies, episodes                      | Movies, episodes                 |
| Stremio          | Movies, shows                    | Movies, shows (target only)      | Movies, episodes                      | Movies, episodes                 |
| Trakt            | Movies, shows                    | Movies, shows, seasons, episodes | Movies, episodes                      | Not a normal sync feature        |
| SIMKL            | Movies, shows, anime             | Movies, shows, anime titles      | Movies, episodes, anime               | Not a normal sync feature        |
| MDBList          | Movies, shows                    | Movies, shows, seasons, episodes | Movies, shows, seasons, episodes      | Not a normal sync feature        |
| PublicMetaDB     | Movies, shows                    | Movies, shows, episodes          | Movies, episodes                      | Movies, episodes                 |
| PunchPlay        | Movies, shows                    | Movies, shows, seasons, episodes | Movies, episodes                      | Movies, episodes                 |
| Scrob            | Movies, shows                    | Movies, shows, seasons, episodes | Movies, episodes, including rewatches | Movies, episodes                 |
| AniList          | Anime movies, anime series       | Anime movies, anime series       | Not supported                         | Not supported                    |
| TMDb             | Movies, shows                    | Movies, shows, episodes          | Not supported                         | Not supported                    |
| Tautulli         | Not supported                    | Not supported                    | Plex movies, episodes                 | Not supported                    |

#### Important restrictions

| Provider         | Notes                                                                         |
| ---------------- | ----------------------------------------------------------------------------- |
| CrossWatch local | Local JSON tracker with snapshots                                             |
| Plex             | Uses Plex Discover. Progress removal can be limited                           |
| Jellyfin         | No native watchlist. Playlist is episode-only. Strict ID matching is optional |
| Emby             | No native watchlist. Playlist is episode-only. Strict ID matching is optional |
| Floppy           | Experimental. Requires TMDb IDs. No progress or playlists                     |
| Nuvio            | Experimental. Watchlist maps to the Nuvio Library. TMDb is recommended        |
| Kodi             | Experimental. Writes need existing library items and unique external IDs      |
| Stremio          | Experimental. Library acts as watchlist. Ratings are target-only              |
| Trakt            | History writes can also add to Trakt Collections                              |
| SIMKL            | No season or episode ratings                                                  |
| MDBList          | Dropped shows can be excluded from history sync                               |
| PublicMetaDB     | Requires TMDb identity. Progress also needs duration                          |
| PunchPlay        | Experimental. Supports Watcher and uses provider IDs for matching             |
| Scrob            | Experimental. Requires a reachable server, API key, and user credentials      |
| AniList          | Anime mapping supports watchlist and ratings only                             |
| TMDb             | Requires a TMDb API key and account session                                   |
| Tautulli         | Read-only source. No writes                                                   |

### Jellyfin and Emby watchlist mapping

| Mode             |                                                 Read |                                                Write | Suitable content                       | Limitations                                  |
| ---------------- | ---------------------------------------------------: | ---------------------------------------------------: | -------------------------------------- | -------------------------------------------- |
| Favorites        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Movies, shows, episodes, library items | Uses the provider Favorite flag              |
| Playlist         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Episodes                               | Does not represent shows. No item migration. |
| Collections      | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Movies, shows, library items           | Uses a named collection                      |
| Native watchlist |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | N/A                                    | No native watchlist                          |

### Authentication and connection methods

#### Authentication

| Provider         | Authentication                        |                                      Refresh support |
| ---------------- | ------------------------------------- | ---------------------------------------------------: |
| CrossWatch local | None                                  |                                                  N/A |
| Plex             | Plex device PIN                       |                                    No refresh action |
| Jellyfin         | Server connection and access token    |                                   Provider-dependent |
| Emby             | Server connection and access token    |                                   Provider-dependent |
| Floppy           | Server URL and API token              |                                                   No |
| Nuvio            | Temporary login code and profile      |                                                  Yes |
| Kodi             | HTTP JSON-RPC with Basic Auth         |                                                  N/A |
| Stremio          | Email and password to obtain auth key |                                                   No |
| Trakt            | OAuth device code                     | <i class="fa-check" style="color:green;">:check:</i> |
| SIMKL            | OAuth                                 |                                   Provider-dependent |
| MDBList          | OAuth device code, legacy API key     |                                        Yes for OAuth |
| PublicMetaDB     | API key                               |                                                   No |
| PunchPlay        | OAuth device code                     |                                                  Yes |
| AniList          | OAuth access token                    |                                   Provider-dependent |
| TMDb             | API key plus user session             |                                        Session-based |
| Tautulli         | Server URL plus API key               |                                                   No |

#### Important connection options

| Provider         | Key options                                                                 |
| ---------------- | --------------------------------------------------------------------------- |
| CrossWatch local | Storage path, retention, snapshots, restore selection                       |
| Plex             | Server, owner, managed users, shared users, Home profile, optional Home PIN |
| Jellyfin         | Server URL, user ID, device ID, optional SSL verification                   |
| Emby             | Server URL, user ID, device ID, optional SSL verification                   |
| Floppy           | Server URL, API token, SSL verification, watchlist name                     |
| Nuvio            | Selected Nuvio profile and stored authentication tokens                     |
| Kodi             | Server URL, HTTP credentials, SSL validation, source-path whitelists        |
| Stremio          | Stored auth key, Like and Love rating thresholds                            |
| Trakt            | Client ID, client secret, access token, refresh token                       |
| SIMKL            | Client ID, client secret, access token                                      |
| MDBList          | Access token, refresh token, API key fallback                               |
| PublicMetaDB     | Base URL                                                                    |
| PunchPlay        | Device-code connection and stored refresh tokens                            |
| AniList          | Client ID, client secret, access token                                      |
| TMDb             | Auto-discovered account ID                                                  |
| Tautulli         | Optional user filter and SSL verification                                   |

#### Authentication notes

1. CrossWatch supports multiple named provider profiles.
2. Plex supports shared tokens and separate friend or shared users.
3. TMDb has two roles: `tmdb` for metadata and `tmdb_sync` for account sync.
4. Provider secrets are redacted from public config responses.


# Pair synchronization

Feature compatibility, matching rules, and sync controls for standard pairs.

This section covers standard pair-based synchronization.

It shows which providers can act as sources or destinations, how matching works, and which safety controls affect writes.

{% hint style="success" %}
Safest first sync:

* one-way
* one feature
* dry run on
* source is a media server
* target is a tracker
  {% endhint %}

{% hint style="warning" %}
Two-way sync and mirror delete mode are powerful.

They can also spread bad matches, conflicts, and deletes.
{% endhint %}

### Feature compatibility

A normal connection is valid for a feature when the selected source can read that feature and the selected destination can write it.

| Feature   | Valid sources                                                                                                                         | Valid destinations                                                                                                                   |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Watchlist | Emby, Jellyfin, Plex; Nuvio, Stremio; AniList, Floppy, MDBList, PublicMetaDB, PunchPlay, Scrob, SIMKL, TMDb, Trakt; CrossWatch local  | Emby, Jellyfin, Plex; Nuvio, Stremio; AniList, Floppy, MDBList, PublicMetaDB, PunchPlay, Scrob, SIMKL, TMDb, Trakt; CrossWatch local |
| Ratings   | Plex; Kodi; AniList, Floppy, MDBList, PublicMetaDB, PunchPlay, Scrob, SIMKL, TMDb, Trakt; CrossWatch local                            | Plex; Kodi, Stremio; AniList, Floppy, MDBList, PublicMetaDB, PunchPlay, Scrob, SIMKL, TMDb, Trakt; CrossWatch local                  |
| History   | Emby, Jellyfin, Plex; Kodi, Nuvio, Stremio; Floppy, MDBList, PublicMetaDB, PunchPlay, Scrob, SIMKL, Trakt; CrossWatch local, Tautulli | Emby, Jellyfin, Plex; Kodi, Nuvio, Stremio; Floppy, MDBList, PublicMetaDB, PunchPlay, Scrob, SIMKL, Trakt; CrossWatch local          |
| Progress  | Emby, Jellyfin, Plex; Kodi, Nuvio, Stremio; Floppy, PublicMetaDB, PunchPlay, Scrob; CrossWatch local                                  | Emby, Jellyfin, Plex; Kodi, Nuvio, Stremio; Floppy, PublicMetaDB, PunchPlay, Scrob; CrossWatch local                                 |

### Pair behavior

<table><thead><tr><th width="274.33331298828125">Pair behavior</th><th width="115.6666259765625" align="right">Supported</th><th>Notes</th></tr></thead><tbody><tr><td>One-way sync</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Source writes to destination</td></tr><tr><td>Two-way sync</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Both sides are compared and synced</td></tr><tr><td>Per-feature enablement</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Watchlist, ratings, history, and progress can be enabled independently</td></tr><tr><td>Per-feature additions</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Add and update writes can be enabled independently</td></tr><tr><td>Per-feature removals</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Removal writes can be enabled independently</td></tr><tr><td>Multiple named provider profiles</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Source and destination profile IDs are preserved</td></tr><tr><td>Dry run</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Plans and logs changes without remote writes</td></tr><tr><td>Verify after write</td><td align="right">Limited</td><td>Runs only when the destination provider supports verification</td></tr></tbody></table>

### Matching and identity resolution

#### Global identity and fallback

<table><thead><tr><th width="203.66668701171875">Capability</th><th width="164.3333740234375">Scope</th><th width="124.3333740234375" align="right">Supported</th><th>Notes</th></tr></thead><tbody><tr><td>Canonical identity mapping</td><td>Global</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Normalizes provider IDs into common keys</td></tr><tr><td>IMDb IDs</td><td>Global</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Used for matching and export</td></tr><tr><td>TMDb IDs</td><td>Global</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Primary identity for PublicMetaDB</td></tr><tr><td>TVDB IDs</td><td>Global</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Used where available</td></tr><tr><td>Trakt IDs</td><td>Global</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Used where available</td></tr><tr><td>SIMKL IDs</td><td>Global</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Used where available</td></tr><tr><td>MDBList IDs</td><td>Global</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Used where available</td></tr><tr><td>Provider native IDs</td><td>Provider-specific</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Preserved for provider-specific matching</td></tr><tr><td>Title and year fallback</td><td>General matching</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Used when strict ID matching is disabled</td></tr></tbody></table>

#### Provider-specific matching

<table><thead><tr><th>Capability</th><th>Scope</th><th width="123" align="right">Supported</th><th>Notes</th></tr></thead><tbody><tr><td>Strict ID matching</td><td>Emby, Jellyfin, Plex</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Disables title and year search fallback</td></tr><tr><td>Plex Discover watchlist matching</td><td>Plex</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Primary Plex watchlist path</td></tr><tr><td>Plex PMS metadata match</td><td>Plex</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Can resolve external IDs through the selected server</td></tr><tr><td>Plex PMS watchlist fallback</td><td>Plex</td><td align="right">Optional</td><td>Disabled by default for strict Discover behavior</td></tr><tr><td>Plex fallback GUID search</td><td>Plex</td><td align="right">Optional</td><td>Searches beyond visible libraries. Best for temporary troubleshooting.</td></tr><tr><td>Stremio IMDb matching</td><td>Stremio</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Uses IMDb IDs. TMDb can enrich missing metadata.</td></tr><tr><td>Floppy TMDb ID matching</td><td>Floppy</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Requires TMDb IDs for supported synchronization.</td></tr></tbody></table>

#### Anime and metadata enrichment

<table><thead><tr><th>Capability</th><th width="169.6666259765625">Scope</th><th width="125" align="right">Supported</th><th>Notes</th></tr></thead><tbody><tr><td>AniBridge local anime mapping</td><td>AniList pairs</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enriches IDs for watchlist and ratings</td></tr><tr><td>Anime-only filtering</td><td>AniList pairs</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Skips content that cannot be confirmed as anime</td></tr><tr><td>Metadata locale</td><td>TMDb metadata</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Default is <code>en-US</code></td></tr><tr><td>Metadata cache</td><td>TMDb metadata</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Default cache lifetime is 720 hours</td></tr></tbody></table>

### Synchronization controls

For runtime behavior, conflict resolution, deletion safeguards, and playlist mappings, see [Synchronization policies](/getting-started/support-matrix/pair-synchronization/synchronization-policies).

#### Write controls

<table><thead><tr><th>Control</th><th width="121.6666259765625" align="right">Supported</th><th>Default or behavior</th></tr></thead><tbody><tr><td>Global addition gate</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enabled</td></tr><tr><td>Global removal gate</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Disabled</td></tr><tr><td>Pair feature additions</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Pair setting overrides the global default</td></tr><tr><td>Pair feature removals</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Pair setting overrides the global default</td></tr><tr><td>Source delete mode</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Removes only when the source deletion is observed</td></tr><tr><td>Mirror delete mode</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Destination is forced to follow the source. Destructive.</td></tr><tr><td>Include observed deletes</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enabled</td></tr><tr><td>Tombstone retention</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Default 1 day</td></tr><tr><td>Dry run</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Disabled</td></tr><tr><td>Verify after write</td><td align="right">Limited</td><td>Disabled. Requires provider support.</td></tr></tbody></table>

#### Safety and guardrails

<table><thead><tr><th>Control</th><th width="124.333251953125" align="right">Supported</th><th>Default or behavior</th></tr></thead><tbody><tr><td>Drop guard</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Disabled</td></tr><tr><td>Mass delete permission</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enabled</td></tr><tr><td>Suspect inventory shrink guard</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Applies after a minimum previous inventory size</td></tr><tr><td>Blackbox unresolved item quarantine</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enabled</td></tr><tr><td>Pair-scoped blackbox</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enabled</td></tr><tr><td>Blackbox addition blocking</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enabled</td></tr><tr><td>Blackbox removal blocking</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Enabled</td></tr><tr><td>Blackbox cooldown</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Default 30 days</td></tr><tr><td>Unresolved and freeze state</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Prevents repeated unsafe writes</td></tr><tr><td>Flapper protection</td><td align="right"><i class="fa-check" style="color:green;">:check:</i></td><td>Integrated with blackbox behavior</td></tr></tbody></table>

#### Runtime and provider behavior

| Control                         | Scope                 | Default or behavior                                     |
| ------------------------------- | --------------------- | ------------------------------------------------------- |
| Provider rate limiting          | All providers         | Provider-specific GET and POST rates                    |
| Retry and backoff               | All providers         | Provider-specific retry budgets and rate-limit handling |
| Apply chunking                  | All providers         | Global and provider-specific chunk sizes                |
| Snapshot reuse cache            | All providers         | Default 300 seconds                                     |
| Checkpoints and watermarks      | Delta-capable flows   | Used for incremental and delta-capable providers        |
| Library filtering               | Emby, Jellyfin, Plex  | Per-feature and scrobble library allowlists             |
| Ignore dropped shows            | MDBList, SIMKL, Trakt | Optional history filter                                 |
| Trakt Collection write          | Trakt                 | Optional when adding history                            |
| Include manually marked watched | Plex                  | Optional. Enabled by default.                           |

#### Progress-specific controls

Progress uses pair thresholds and Destination completion policies.

See [Playback progress thresholds and completion policies](/getting-started/support-matrix/pair-synchronization/synchronization-policies) for defaults, provider limits, and manual editing rules.


# Synchronization policies

Runtime policies for pair sync, deletions, conflicts, playlists, and provider capabilities.

{% tabs %}
{% tab title="End users" %}
Use this reference to understand what CrossWatch (CW) reads, writes, and removes.

{% hint style="warning" %}
Two-way sync and mirror removals can spread bad matches and unwanted deletes.
{% endhint %}

CW evaluates support at runtime. It skips unavailable features and unsupported writes.

### Start here

| If you want to…                       | Use this         |
| ------------------------------------- | ---------------- |
| Copy one provider into another        | **One-way** sync |
| Keep both providers updated           | **Two-way** sync |
| Keep Destination-only items           | `source_deletes` |
| Make Destination match Source exactly | `mirror`         |
| Sync a playlist safely                | `managed_only`   |

{% hint style="warning" %}
Two-way sync and `mirror` can spread bad matches and unwanted deletions.
{% endhint %}

### What CW does

**Watchlist** copies missing items. Removals need explicit permission.

**History** copies supported watch events. State-only providers preserve less event detail.

**Ratings** use the Source in one-way pairs. Two-way pairs prefer newer ratings.

**Progress** avoids moving resume positions backwards without newer timestamps.

### Safe defaults

* Keep **Remove** disabled and use one-way syncs.
* Use **Dry run** before enabling writes.
* Start with a media server as the Source.

See [Configure Pairs](/crosswatch/settings/configure-pairs) for pair setup and [Global settings](/crosswatch/settings/configure-pairs/global-settings) for safety controls.

### At a glance

* **One-way**: the Source defines the desired state.
* **Two-way**: both providers contribute state.
* **Removals**: disabled by default and guarded by baselines.
* **Playlists**: use explicit mappings, not standard feature sync.

### Terminology

<table><thead><tr><th width="190.66668701171875">Term</th><th>Meaning</th></tr></thead><tbody><tr><td><strong>Source</strong></td><td>The left provider. In two-way sync, it is the final ratings and progress fallback.</td></tr><tr><td><strong>Destination</strong></td><td>The right provider. One-way pairs write Source changes here.</td></tr><tr><td><strong>Snapshot</strong></td><td>Current provider items for one feature.</td></tr><tr><td><strong>Baseline</strong></td><td>Last stable provider state. It detects real additions and removals.</td></tr><tr><td><strong>Observed deletion</strong></td><td>A prior-baseline item absent from a reliable current snapshot.</td></tr><tr><td><strong>Tombstone</strong></td><td>Stored delete marker that prevents immediate restoration.</td></tr><tr><td><strong>Managed item</strong></td><td>An item CW tracks for a playlist mapping.</td></tr></tbody></table>

### Identity and matching

CW normalizes provider data into canonical items before comparison.

Identifier preference order:

1. TMDb
2. IMDb
3. TVDB
4. ..and the rest

Movies and shows use the strongest available identifier. Seasons use a show identifier and season number. Episodes also use season and episode coordinates.

When no stable identifier exists, CW can match media type, title, and year. This fallback is less reliable. Recognized alternative identifiers can still match items with different primary keys.

Anime mapping can rekey or enrich items before comparison. Enable it for eligible pairs.

### Planning and safety

#### Adds and removals

Adds are enabled by default. Removals are disabled by default.

```yaml
sync:
  enable_add: true
  enable_remove: false
```

Each feature can override these defaults with `add` and `remove`.

#### Provider health and snapshots

CW checks provider health and feature availability before planning writes. Authentication failures skip the affected pair.

One-way pairs plan no writes when the Source is unavailable. Missing snapshots never trigger removals when either provider is unavailable.

A present snapshot represents the complete current feature state. A delta snapshot contains recent changes only. CW combines delta snapshots with the prior baseline.

#### First two-way run

The first two-way run merges both sides. It never propagates removals because no baseline proves a deliberate deletion.

#### Scope and guardrails

History, ratings, and progress can use selected provider libraries. Items outside scope are excluded from reads and writes.

Manual additions are merged into effective provider state. Manual blocks prevent matching additions, updates, and removals.

The drop guard detects suspicious snapshot reductions. It can use the last stable baseline for planning. This protects against temporary failures and incomplete responses.

Mass-deletion protection can block unsafe removal plans. Playlist mappings use the same protection.

#### Unresolved items and write confirmation

Items that cannot be written are stored as unresolved. CW retries their additions, updates, and removals later.

An unresolved item clears only after sufficient success evidence. CW prefers confirmed item keys. It uses provider counts conservatively when keys are unavailable.

Providers with verification support can refresh after a write to confirm the resulting state.

### One-way synchronization

One-way synchronization runs from Source to Destination. The Source is authoritative, subject to removal mode, timestamps, capabilities, and thresholds.

{% hint style="info" %}
Use one-way sync when one provider should remain authoritative.
{% endhint %}

#### Watchlist

CW adds Source watchlist items missing from the Destination. The default removal mode is `source_deletes`.

In `source_deletes` mode, CW removes a Destination item only when:

1. It existed in the previous Source baseline.
2. It is absent from the current Source snapshot.
3. The snapshot is reliable.
4. Removals are enabled.

This retains Destination items that the Source never managed. In `mirror` mode, CW can remove every Destination item absent from the Source. Safeguards still apply.

#### History

History synchronizes watch events when both providers expose timestamps. State-only providers synchronize watched history without separate rewatch events.

CW copies missing Source events. With Trakt, timestamps in the same 60-second bucket represent one event. Different timestamps represent separate rewatches.

Entries without usable watch timestamps are not copied as watch events. History removals use the selected one-way removal mode.

#### Ratings

When ratings differ, CW writes the Source rating to the Destination. Media types and rating date can limit this behavior.

Unrating uses the selected removal policy. The default only propagates an unrating after CrossWatch observes a prior Source rating disappear.

#### Playback progress

CW writes meaningful Source resume positions.

CW skips positions that are too small, too similar, or near completion. See [Playback progress synchronization](#playback-progress-synchronization) for thresholds and provider safety caps.

### Two-way synchronization

Two-way synchronization compares both providers and can write to each. The left side does not always win.

{% hint style="warning" %}
Use two-way sync only when matching is reliable on both providers.
{% endhint %}

#### Watchlist

Watchlist synchronization is presence-based:

1. An item on only one side is added to the other.
2. An item on both sides needs no action.
3. A confirmed deletion on either side removes the other copy when removals are enabled.

Deletion handling is symmetric. The initial run merges both watchlists and suppresses removals.

#### History

History is event-based when both providers expose watch timestamps:

1. Copy an event that exists on only one side.
2. Treat matching item and timestamp as one event.
3. With Trakt, timestamps within 60 seconds are one event.
4. Treat different timestamps as separate events.
5. Propagate confirmed deletions when removals are enabled.
6. Synchronize a history event newer than its tombstone.

History has no Source value winner. Distinct timestamps represent distinct events.

#### Ratings

When ratings differ, CW resolves the conflict in this order:

1. Newest `rated_at` when both timestamps differ.
2. `sync.bidirectional.source_of_truth`.
3. Pair Source as the final fallback.

A missing rating is not automatically a deletion. CW needs an observed deletion or another valid removal signal.

#### Playback progress

CW prefers newer progress. It then prefers the furthest position. See **Power users** for the full conflict order.

### Playback progress synchronization

CW synchronizes resume positions separately from watched history. Progress is intended for partially watched movies and episodes. Items close to completion are excluded from progress synchronization so that History can manage their watched state.

#### Default thresholds

Unless a pair contains explicit values, the CrossWatch orchestrator uses these defaults:

<table><thead><tr><th width="232.33331298828125">Setting</th><th width="114.3333740234375" align="right">Default</th><th>Purpose</th></tr></thead><tbody><tr><td>Minimum progress</td><td align="right">60 seconds</td><td>Ignores accidental starts and very small resume positions</td></tr><tr><td>Change threshold</td><td align="right">30 seconds</td><td>Avoids writes when the difference between providers is insignificant</td></tr><tr><td>Near completion threshold</td><td align="right">95%</td><td>Stops synchronizing progress near completion and leaves the watched state to History</td></tr><tr><td>Timestamp tolerance</td><td align="right">30 seconds</td><td>Protects a meaningfully newer target position from being overwritten</td></tr><tr><td>Progress removal</td><td align="right">Disabled</td><td>Resume positions are not cleared unless removal is enabled</td></tr><tr><td>Replay watched items</td><td align="right">Disabled</td><td>Watched items are not unwatched to apply a resume position unless replay is enabled</td></tr></tbody></table>

#### Near completion behavior

The `max_percent` setting defines when CrossWatch stops treating an item as resumable progress.

With the default value of `95`:

* Progress at 94% can be synchronized.
* Progress at 95% or higher is ignored by Progress synchronization.
* History synchronization manages the watched state.

The configured value can be changed per pair.

#### Provider completion limits

Some providers mark an item watched when a high resume position is written. CrossWatch applies the provider's declared completion limit as a safety cap.

The effective threshold is:

```
minimum of the configured max_percent and the target provider limit
```

Examples:

* Configured threshold 95%, provider limit 80% → effective threshold 80%.
* Configured threshold 85%, provider limit 90% → effective threshold 85%.
* Configured threshold 95%, no provider limit → effective threshold 95%.

A user-configured lower threshold is always respected. A higher value cannot override a provider safety limit.

#### One-way pairs

In a one-way pair, CrossWatch calculates the effective threshold for the Destination provider.

```
Plex → PublicMetaDB
Configured max_percent: 95%
PublicMetaDB completion limit: 80%
Effective threshold: 80%
```

Progress below 80% can be written to PublicMetaDB. Progress at 80% or higher is left to History synchronization.

#### Two-way pairs

A two-way pair stores one shared `max_percent`. CrossWatch calculates the effective threshold independently for each direction.

```
Plex ↔ PublicMetaDB
Configured max_percent: 95%

Writes to Plex: 90% effective threshold
Writes to PublicMetaDB: 80% effective threshold
```

The pair Source does not determine these thresholds. Each direction uses its target provider's capabilities.

#### Provider summary

* **Stremio:** keeps one active episode resume position per series.
* **PublicMetaDB:** uses an 80% safety cap.
* **Nuvio:** uses a 90% safety cap for normal-length content.
* **Emby, Jellyfin, Kodi, and Plex:** recommend a 90% cap.
* **Floppy:** uses the pair value, normally 95%.
* **PunchPlay:** uses the pair value, normally 95%.
* **Other providers:** use the pair value, normally 95%.

See **Power users** for provider-specific behavior and configuration.

### Removals and deletion safeguards

Deletion is more conservative than addition.

| Rule                | Behavior                                                   |
| ------------------- | ---------------------------------------------------------- |
| Default             | Adds are enabled. Removals are disabled.                   |
| Required evidence   | A removal normally needs a baseline and reliable snapshot. |
| Unsupported signals | Providers can disable observed deletions per feature.      |
| Two-way deletes     | A deletion creates a tombstone.                            |
| First two-way run   | No removals are applied.                                   |
| Failure protection  | Failed, suspicious, or incomplete reads suppress removals. |
| {% endtab %}        |                                                            |

{% tab title="Power users" %}

### Configuration summary

```yaml
sync:
  enable_add: true
  enable_remove: false
  include_observed_deletes: true
  drop_guard: false
  allow_mass_delete: true
  verify_after_write: false
  one_way_remove_mode: source_deletes
  bidirectional:
    source_of_truth: ""
```

Feature settings can override global values:

```yaml
features:
  watchlist:
    add: true
    remove: false
  history:
    add: true
    remove: false
  ratings:
    add: true
    remove: false
    types: [movie, show, episode]
    from_date: ""
  progress:
    add: true
    remove: false
    min_seconds: 60
    delta_seconds: 30
    max_percent: 95
    timestamp_tolerance_seconds: 30
    replay_enabled: false
    propagate_timestamp_updates: false
```

### Playback progress details

Pair defaults are normalized when the pair is saved. The progress planner also applies them when values are missing.

#### Provider defaults and limits

| Provider                 | Progress write policy                               |        Effective default |
| ------------------------ | --------------------------------------------------- | -----------------------: |
| Emby                     | Controlled by `LibraryOptions.MaxResumePct`         | Pair value, normally 95% |
| Jellyfin                 | Controlled by `MaxResumePct`                        |       90% recommendation |
| Plex                     | Controlled by `Video played threshold`              |       90% recommendation |
| Kodi                     | Controlled by `playcountminimumpercent`             |       90% recommendation |
| Nuvio                    | Automatically completes normal-length items         |                      90% |
| Stremio                  | Keeps one active episode resume position per series | Pair value, normally 95% |
| Floppy                   | No documented provider completion policy            | Pair value, normally 95% |
| PunchPlay                | No documented provider completion policy            | Pair value, normally 95% |
| PublicMetaDB             | Automatically completes progress writes             |                      80% |
| CrossWatch local tracker | No provider completion policy                       | Pair value, normally 95% |

#### Scrobble thresholds

Trakt, SIMKL, and MDBList use an 80% threshold for stop-scrobble behavior.

This does not limit normal progress writes. CrossWatch does not lower `max_percent` to 80% for these providers.

#### Nuvio duration rule

Nuvio applies its 90% completion cap only when duration is at least 60 seconds.

For shorter known items, CrossWatch does not apply the cap. Unknown durations use the safer 90% threshold.

#### Two-way conflict handling

When progress differs, CrossWatch uses this priority:

1. Newest `progress_at`.
2. Furthest position in milliseconds.
3. Highest progress percentage.
4. `sync.bidirectional.source_of_truth`.
5. Pair Source as the final fallback.

For a clear versus progress conflict, the newest timestamp wins. Equal or missing timestamps use the configured source of truth, then the pair Source.

Kodi first-observed timestamps are not reliable conflict timestamps. A real timestamp from the other provider wins.

#### Regression protection and clears

CrossWatch normally avoids moving an item backwards. A lower Source position is written only when its timestamp is clearly newer.

Without reliable timestamps, CrossWatch keeps the furthest known progress. A clear is zero progress.

CrossWatch propagates clears only when removal is enabled and a reliable deletion or clear signal exists. History manages items near the completion threshold.

Playlist mappings use:

```yaml
membership: managed_only
order: ignore
allow_mass_delete: false
```

{% endtab %}
{% endtabs %}


# Scrobbling

Real-time Watcher and webhook support, routes, and playback rules.

This section covers real-time scrobbling support.

It shows route compatibility, Watcher and webhook behavior, and the progress thresholds that affect scrobble events.

{% hint style="success" %}
Use Watcher when possible. Scrob can be either a source or a sink.
{% endhint %}

### Quick links

<table data-view="cards"><thead><tr><th>Title</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td>Choose between Watcher and webhooks</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/1Dx9OZYvOJ6utvcFoU2A">/spaces/3rh5THg1PdhVsBt3GALo/pages/1Dx9OZYvOJ6utvcFoU2A</a></td></tr><tr><td>Set up Watcher routes</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/J9gNWTn8GfZMQxOd5AW2">/spaces/3rh5THg1PdhVsBt3GALo/pages/J9gNWTn8GfZMQxOd5AW2</a></td></tr><tr><td>Set up webhook mappings</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/BxJJk2jkTcCo8ZkED8lI">/spaces/3rh5THg1PdhVsBt3GALo/pages/BxJJk2jkTcCo8ZkED8lI</a></td></tr><tr><td>Filter by user, server, or library</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/PH2jfcZjseHr6sFkoKAP">/spaces/3rh5THg1PdhVsBt3GALo/pages/PH2jfcZjseHr6sFkoKAP</a></td></tr></tbody></table>

### Route support

Floppy and Stremio do not provide Watcher or webhook playback sources.

#### Watcher routes

| Playback source |                                              MDBList |                                            PunchPlay |                                                Scrob |                                                SIMKL |                                                Trakt |
| --------------- | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: |
| Emby            | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Jellyfin        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Plex            | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Kodi            | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Scrob           | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |                                                  N/A | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |

#### Webhook routes

| Playback source |                                            MDBList |                                                Scrob |                                              SIMKL |                                                Trakt |
| --------------- | -------------------------------------------------: | ---------------------------------------------------: | -------------------------------------------------: | ---------------------------------------------------: |
| Emby            | <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Jellyfin        | <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Plex            | <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |

Scrob is a webhook sink only. It cannot be a webhook source.

### Scrobbling capabilities

#### Core behavior

| Capability                                 |                                              Watcher |                                              Webhook | Notes                                                           |
| ------------------------------------------ | ---------------------------------------------------: | ---------------------------------------------------: | --------------------------------------------------------------- |
| Independent enablement                     | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Watcher and webhook can be enabled independently                |
| Automatic start on app boot                | <i class="fa-check" style="color:green;">:check:</i> |                                                  N/A | Available for the watcher source                                |
| Route-based configuration                  | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Routes connect a media source profile to a tracker sink profile |
| Start, pause, and stop progress handling   | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Sink thresholds control pause versus completed stop             |
| Pause debounce                             | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Suppresses very short pause events                              |
| Near-completion start suppression          | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Suppresses credit or near-end start flapping                    |
| Automatic watchlist removal after watching | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Configurable for movie, show, and episode types                 |

#### Filtering and routing

| Capability                 |                                              Watcher |                                              Webhook | Notes                                          |
| -------------------------- | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------- |
| Username allowlist         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Plex account or user filtering                 |
| Server UUID allowlist      | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Empty means unrestricted unless blocked        |
| Server UUID blocklist      | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Always ignored when matched                    |
| Media library allowlist    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Available per Plex, Jellyfin, and Emby profile |
| Kodi source-path allowlist | <i class="fa-check" style="color:green;">:check:</i> |                                                  N/A | Configured in the Kodi connection profile      |

#### Plex-specific behavior

| Capability                         |                                              Watcher |                                              Webhook | Notes                                                     |
| ---------------------------------- | ---------------------------------------------------: | ---------------------------------------------------: | --------------------------------------------------------- |
| Autoplay suppression               |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | Webhook-only protection for short autoplay transitions    |
| Live session progress probing      |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | Webhook-only session probing to improve progress accuracy |
| Plex rating forwarding to Trakt    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | Optional                                                  |
| Plex rating forwarding to SIMKL    | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | Optional watcher behavior                                 |
| Plex rating forwarding to MDBList  | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | Optional watcher behavior                                 |
| Plex Pass requirement for webhooks |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | Required by Plex to send webhook events                   |

#### Emby-specific behavior

| Capability                             |                                            Watcher |                                              Webhook | Notes                                   |
| -------------------------------------- | -------------------------------------------------: | ---------------------------------------------------: | --------------------------------------- |
| Emby Premiere requirement for webhooks | <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> | Required by Emby to send webhook events |

### Scrobble progress rules

{% hint style="info" %}
The default thresholds reduce noisy updates and prevent early stops from marking items watched.
{% endhint %}

| Rule                                   |    Default | Purpose                                               |
| -------------------------------------- | ---------: | ----------------------------------------------------- |
| Progress reporting step (Watcher only) | 25 percent | Reduces unnecessary sink updates from watcher polling |
| Stop becomes pause below               | 80 percent | Prevents an early stop from marking content watched   |
| Forced completed stop at               | 95 percent | Treats near completion as watched                     |
| Allowed progress regression            |  5 percent | Tolerates small backward progress changes             |
| Pause debounce                         |  5 seconds | Ignores micro-pauses                                  |
| Near-end start suppression             | 99 percent | Suppresses credit-related start flapping              |


# Playback Progress Manager

Resume-item provider support, bulk actions, and live playback visibility.

The Playback Progress Manager is separate from normal pair-based progress sync.

Use it to inspect and manage resume items across supported providers.

{% hint style="info" %}
This tool works across more providers than pair-based progress sync.
{% endhint %}

### Provider support

#### Actions

| Provider     |                                    Read resume items |                                      Update progress |                                      Remove progress |                                         Mark watched |                                         Bulk actions |
| ------------ | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: |
| Plex         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Jellyfin     | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Emby         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Nuvio        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Kodi         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Trakt        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| SIMKL        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| MDBList      | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| PublicMetaDB | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |

#### Media and live support

| Provider     |                                               Movies |                                             Episodes |                                          Anime aware |                                  Live status overlay |
| ------------ | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: |
| Plex         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Jellyfin     | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Emby         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| Nuvio        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Kodi         | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Trakt        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| SIMKL        | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| MDBList      | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| PublicMetaDB | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |

### Manager capabilities

| Manager capability                        |                                            Supported | Notes                                               |
| ----------------------------------------- | ---------------------------------------------------: | --------------------------------------------------- |
| Combine progress from multiple providers  | <i class="fa-check" style="color:green;">:check:</i> | Uses canonical identity and fallback grouping       |
| Provider profile filtering                | <i class="fa-check" style="color:green;">:check:</i> | Individual profiles can be excluded                 |
| Force refresh                             | <i class="fa-check" style="color:green;">:check:</i> | Bypasses the short-lived progress cache             |
| Live playing, paused, and buffering state | <i class="fa-check" style="color:green;">:check:</i> | Overlaid for Plex, Jellyfin, and Emby               |
| Sort by last updated                      | <i class="fa-check" style="color:green;">:check:</i> |                                                     |
| Sort by highest or lowest progress        | <i class="fa-check" style="color:green;">:check:</i> |                                                     |
| Sort by remaining time                    | <i class="fa-check" style="color:green;">:check:</i> |                                                     |
| Sort by rating                            | <i class="fa-check" style="color:green;">:check:</i> |                                                     |
| Sort by title                             | <i class="fa-check" style="color:green;">:check:</i> |                                                     |
| Sort by provider                          | <i class="fa-check" style="color:green;">:check:</i> |                                                     |
| Remaining time calculation                | <i class="fa-check" style="color:green;">:check:</i> | Requires a usable duration                          |
| Poster and backdrop enrichment            | <i class="fa-check" style="color:green;">:check:</i> | Uses provider data and TMDb metadata when available |
| Provider timeout                          | <i class="fa-check" style="color:green;">:check:</i> | Aggregate timeout is configurable                   |


# Tooling and workflows

App tooling, Quick Add, captures, backups, exports, scheduler, dashboard, analyzer, editor, and maintenance.

This section covers the main CrossWatch tools and workflows outside of standard syncing.

{% hint style="info" %}
Use these tools to debug, recover, and handle edge cases without changing your main pair design.
{% endhint %}

### Application tooling matrix

| Tool or screen          |                                            Supported | Main capabilities                                                                |
| ----------------------- | ---------------------------------------------------: | -------------------------------------------------------------------------------- |
| Connections             | <i class="fa-check" style="color:green;">:check:</i> | Configure providers, profiles, and pairs                                         |
| Synchronization         | <i class="fa-check" style="color:green;">:check:</i> | Run all pairs or selected pairs in one-way or two-way mode                       |
| Unified Watchlist       | <i class="fa-check" style="color:green;">:check:</i> | Combine configured watchlists into one view                                      |
| Quick Add               | <i class="fa-check" style="color:green;">:check:</i> | Search TMDb and write history, watchlist, and ratings                            |
| Playback Progress       | <i class="fa-check" style="color:green;">:check:</i> | Inspect, update, clear, and complete resume items                                |
| Captures                | <i class="fa-check" style="color:green;">:check:</i> | Create, inspect, compare, restore, and delete captures                           |
| Analyzer                | <i class="fa-check" style="color:green;">:check:</i> | Diagnose state, artifacts, watermarks, unresolved items, and blackbox issues     |
| Editor                  | <i class="fa-check" style="color:green;">:check:</i> | Inspect and edit tracker state, provider state, pair datasets, and manual blocks |
| Backup and Restore      | <i class="fa-check" style="color:green;">:check:</i> | Create, validate, upload, download, restore, schedule, and retain ZIP backups    |
| Export                  | <i class="fa-check" style="color:green;">:check:</i> | Create import files for supported external workflows                             |
| Scheduler               | <i class="fa-check" style="color:green;">:check:</i> | Run standard schedules, advanced jobs, and manual triggers                       |
| Activity                | <i class="fa-check" style="color:green;">:check:</i> | Recent sync and scrobble activity                                                |
| Output                  | <i class="fa-check" style="color:green;">:check:</i> | Separate Sync, Watcher, and Debug tabs                                           |
| Health and probes       | <i class="fa-check" style="color:green;">:check:</i> | Provider configuration, authentication, and health checks                        |
| Dashboard               | <i class="fa-check" style="color:green;">:check:</i> | Configurable media and activity widgets                                          |
| Maintenance             | <i class="fa-check" style="color:green;">:check:</i> | Clear caches and state, inspect local tracker, reset data, and restart           |
| Local tracker           | <i class="fa-check" style="color:green;">:check:</i> | CrossWatch itself can be a sync endpoint                                         |
| Metadata resolver       | <i class="fa-check" style="color:green;">:check:</i> | TMDb metadata, images, IDs, score, and runtime enrichment                        |
| Anime mapping           | <i class="fa-check" style="color:green;">:check:</i> | Local AniBridge data for AniList watchlist and ratings                           |
| Statistics              | <i class="fa-check" style="color:green;">:check:</i> | Runtime and sync statistics                                                      |
| Local UI authentication | <i class="fa-check" style="color:green;">:check:</i> | Optional username and password authentication                                    |
| HTTPS                   | <i class="fa-check" style="color:green;">:check:</i> | Self-signed or custom TLS certificate                                            |
| Telemetry toggle        | <i class="fa-check" style="color:green;">:check:</i> | Usage telemetry can be disabled                                                  |

### Quick Add and manual writes

| Capability                   |                                            Supported | Notes                                            |
| ---------------------------- | ---------------------------------------------------: | ------------------------------------------------ |
| TMDb movie search            | <i class="fa-check" style="color:green;">:check:</i> | Requires a TMDb API key                          |
| TMDb show search             | <i class="fa-check" style="color:green;">:check:</i> | Requires a TMDb API key                          |
| Write watched history        | <i class="fa-check" style="color:green;">:check:</i> | Only offered to providers with history support   |
| Add to watchlist             | <i class="fa-check" style="color:green;">:check:</i> | Only offered to providers with watchlist support |
| Write a rating               | <i class="fa-check" style="color:green;">:check:</i> | Rating range is 1 to 10                          |
| Select multiple providers    | <i class="fa-check" style="color:green;">:check:</i> | Provider-instance aware                          |
| Watched date today           | <i class="fa-check" style="color:green;">:check:</i> | Uses the current UTC timestamp                   |
| Custom watched date          | <i class="fa-check" style="color:green;">:check:</i> | Uses a selected calendar date                    |
| Release date as watched date | <i class="fa-check" style="color:green;">:check:</i> | Fetches the release or first-air date from TMDb  |
| External ID enrichment       | <i class="fa-check" style="color:green;">:check:</i> | Adds TMDb, IMDb, and TVDB IDs when available     |

### Captures and snapshots

| Capability                         |                                            Supported | Notes                                                                      |
| ---------------------------------- | ---------------------------------------------------: | -------------------------------------------------------------------------- |
| Capture watchlist                  | <i class="fa-check" style="color:green;">:check:</i> | Provider and profile-aware                                                 |
| Capture ratings                    | <i class="fa-check" style="color:green;">:check:</i> | Provider and profile-aware                                                 |
| Capture history                    | <i class="fa-check" style="color:green;">:check:</i> | Provider and profile-aware                                                 |
| Capture progress                   | <i class="fa-check" style="color:green;">:check:</i> | Provider and profile-aware                                                 |
| Full capture                       | <i class="fa-check" style="color:green;">:check:</i> | Can contain multiple feature child captures                                |
| List captures                      | <i class="fa-check" style="color:green;">:check:</i> |                                                                            |
| Read capture content               | <i class="fa-check" style="color:green;">:check:</i> |                                                                            |
| Basic comparison                   | <i class="fa-check" style="color:green;">:check:</i> | Added, removed, and changed summaries                                      |
| Extended comparison                | <i class="fa-check" style="color:green;">:check:</i> | Feature selection, search, paging, kind filters, and nested change details |
| Restore by merge                   | <i class="fa-check" style="color:green;">:check:</i> | Adds or updates capture data                                               |
| Clear and restore                  | <i class="fa-check" style="color:green;">:check:</i> | Clears the destination feature first                                       |
| Delete capture                     | <i class="fa-check" style="color:green;">:check:</i> | Optional deletion of child captures                                        |
| Clear selected provider features   | <i class="fa-check" style="color:green;">:check:</i> | Maintenance operation                                                      |
| Local CrossWatch tracker snapshots | <i class="fa-check" style="color:green;">:check:</i> | Automatic snapshots before local changes                                   |
| Local retention                    | <i class="fa-check" style="color:green;">:check:</i> | Days and maximum snapshot count are configurable                           |

### Backup and restore

| Capability                    |                                            Supported | Notes                        |
| ----------------------------- | ---------------------------------------------------: | ---------------------------- |
| Create manual backup          | <i class="fa-check" style="color:green;">:check:</i> | ZIP archive                  |
| Application state scope       | <i class="fa-check" style="color:green;">:check:</i> | Default scope                |
| Include captures              | <i class="fa-check" style="color:green;">:check:</i> | Optional                     |
| Include reports               | <i class="fa-check" style="color:green;">:check:</i> | Optional                     |
| Include cache                 | <i class="fa-check" style="color:green;">:check:</i> | Optional                     |
| List backups                  | <i class="fa-check" style="color:green;">:check:</i> |                              |
| Download backup               | <i class="fa-check" style="color:green;">:check:</i> |                              |
| Upload backup                 | <i class="fa-check" style="color:green;">:check:</i> | ZIP only, size-limited       |
| Validate before restore       | <i class="fa-check" style="color:green;">:check:</i> |                              |
| Restore backup                | <i class="fa-check" style="color:green;">:check:</i> | Creates a pre-restore backup |
| Restart after restore         | <i class="fa-check" style="color:green;">:check:</i> | Optional                     |
| Delete backup                 | <i class="fa-check" style="color:green;">:check:</i> |                              |
| Scheduled backups             | <i class="fa-check" style="color:green;">:check:</i> | Time and day selection       |
| Retention by age              | <i class="fa-check" style="color:green;">:check:</i> |                              |
| Retention by maximum count    | <i class="fa-check" style="color:green;">:check:</i> |                              |
| Automatic old backup deletion | <i class="fa-check" style="color:green;">:check:</i> | Optional                     |

### Export compatibility

#### Feature support by export target

| Export target |                                            Watchlist |                                              History |                                              Ratings |                         Combined history and ratings |
| ------------- | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: | ---------------------------------------------------: |
| Letterboxd    | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| IMDb          | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| JustWatch     | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |
| Yamtrack      | <i class="fa-check" style="color:green;">:check:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |
| TMDb workflow | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> | <i class="fa-check" style="color:green;">:check:</i> |   <i class="fa-xmark" style="color:red;">:xmark:</i> |

#### Media support and limitations

| Export target | Media types                                             | Important limitations                                                |
| ------------- | ------------------------------------------------------- | -------------------------------------------------------------------- |
| Letterboxd    | Movies only                                             | Watched dates are normalized. Files over 1 MB may require splitting. |
| IMDb          | Rows with an IMDb ID                                    | Watchlist only                                                       |
| JustWatch     | Movies, shows, seasons, episodes                        | Export file, not a direct API sync provider                          |
| Yamtrack      | Movies, shows, seasons, episodes                        | Rating-only rows in combined export are skipped                      |
| TMDb workflow | Movies, shows, seasons, episodes where identity permits | Produces account-import-compatible CSV structures                    |

| Export control                 |                                            Supported |
| ------------------------------ | ---------------------------------------------------: |
| Select source provider         | <i class="fa-check" style="color:green;">:check:</i> |
| Select source profile          | <i class="fa-check" style="color:green;">:check:</i> |
| Select feature                 | <i class="fa-check" style="color:green;">:check:</i> |
| Select media types             | <i class="fa-check" style="color:green;">:check:</i> |
| Search and filter rows         | <i class="fa-check" style="color:green;">:check:</i> |
| Preview compatibility warnings | <i class="fa-check" style="color:green;">:check:</i> |
| Missing identity filtering     | <i class="fa-check" style="color:green;">:check:</i> |
| Invalid watched date reporting | <i class="fa-check" style="color:green;">:check:</i> |

### Scheduler

| Schedule mode          |                                            Supported | Notes                                    |
| ---------------------- | ---------------------------------------------------: | ---------------------------------------- |
| Hourly                 | <i class="fa-check" style="color:green;">:check:</i> | Standard scheduler                       |
| Every N hours          | <i class="fa-check" style="color:green;">:check:</i> | Default is every 12 hours                |
| Daily at a fixed time  | <i class="fa-check" style="color:green;">:check:</i> | 24-hour time                             |
| Custom minute interval | <i class="fa-check" style="color:green;">:check:</i> | Minimum 15 minutes                       |
| Advanced jobs          | <i class="fa-check" style="color:green;">:check:</i> | Multiple targeted jobs                   |
| Scheduled backups      | <i class="fa-check" style="color:green;">:check:</i> | Stored under advanced scheduling         |
| Trigger now            | <i class="fa-check" style="color:green;">:check:</i> | Can execute a provided scheduler payload |
| Stop scheduler         | <i class="fa-check" style="color:green;">:check:</i> |                                          |
| Replan next run        | <i class="fa-check" style="color:green;">:check:</i> | Refreshes the live worker                |
| Show status            | <i class="fa-check" style="color:green;">:check:</i> |                                          |
| Show next run          | <i class="fa-check" style="color:green;">:check:</i> |                                          |

### Dashboard and interface

| Capability                           |                                            Supported | Notes                                                        |
| ------------------------------------ | ---------------------------------------------------: | ------------------------------------------------------------ |
| Flat dark theme                      | <i class="fa-check" style="color:green;">:check:</i> |                                                              |
| Flat light theme                     | <i class="fa-check" style="color:green;">:check:</i> |                                                              |
| Original theme                       | <i class="fa-check" style="color:green;">:check:</i> |                                                              |
| Watchlist preview                    | <i class="fa-check" style="color:green;">:check:</i> | Configurable                                                 |
| Now Playing card                     | <i class="fa-check" style="color:green;">:check:</i> | Configurable                                                 |
| Recent scrobble activity             | <i class="fa-check" style="color:green;">:check:</i> | Configurable                                                 |
| Recent history widget                | <i class="fa-check" style="color:green;">:check:</i> | Configurable                                                 |
| Latest ratings widget                | <i class="fa-check" style="color:green;">:check:</i> | Configurable                                                 |
| Recent sync widget                   | <i class="fa-check" style="color:green;">:check:</i> | Count or time-window display                                 |
| Configurable dashboard media widgets | <i class="fa-check" style="color:green;">:check:</i> | Added on current `main`                                      |
| Quick Add desktop drawer             | <i class="fa-check" style="color:green;">:check:</i> | Configurable                                                 |
| Quick Add mobile button              | <i class="fa-check" style="color:green;">:check:</i> | Configurable                                                 |
| Sync output tab                      | <i class="fa-check" style="color:green;">:check:</i> | Sync execution output                                        |
| Watcher output tab                   | <i class="fa-check" style="color:green;">:check:</i> | Watcher and scrobble output                                  |
| Debug output tab                     | <i class="fa-check" style="color:green;">:check:</i> | Provider diagnostics mirrored into the UI                    |
| Docker stdout diagnostics            | <i class="fa-check" style="color:green;">:check:</i> | Debug diagnostics continue to be written to container output |
| Responsive web interface             | <i class="fa-check" style="color:green;">:check:</i> | Desktop and mobile controls are present                      |

### Analyzer coverage

| Diagnostic area                                 |                                            Supported |
| ----------------------------------------------- | ---------------------------------------------------: |
| Main state file                                 | <i class="fa-check" style="color:green;">:check:</i> |
| Manual policy state                             | <i class="fa-check" style="color:green;">:check:</i> |
| Pair-scoped state files                         | <i class="fa-check" style="color:green;">:check:</i> |
| Provider watermarks                             | <i class="fa-check" style="color:green;">:check:</i> |
| Shadow state                                    | <i class="fa-check" style="color:green;">:check:</i> |
| Provider indexes                                | <i class="fa-check" style="color:green;">:check:</i> |
| Unresolved state                                | <i class="fa-check" style="color:green;">:check:</i> |
| Flap state                                      | <i class="fa-check" style="color:green;">:check:</i> |
| Blackbox state                                  | <i class="fa-check" style="color:green;">:check:</i> |
| Provider sync modules                           | <i class="fa-check" style="color:green;">:check:</i> |
| Orchestrator modules                            | <i class="fa-check" style="color:green;">:check:</i> |
| Artifact parse failures                         | <i class="fa-check" style="color:green;">:check:</i> |
| Error, warning, and information severity totals | <i class="fa-check" style="color:green;">:check:</i> |
| Category and problem type totals                | <i class="fa-check" style="color:green;">:check:</i> |
| Multiple selected pairs                         | <i class="fa-check" style="color:green;">:check:</i> |
| Safe path validation                            | <i class="fa-check" style="color:green;">:check:</i> |

### Editor coverage

| Editor source                                       |                                            Supported | Operations                            |
| --------------------------------------------------- | ---------------------------------------------------: | ------------------------------------- |
| CrossWatch tracker data                             | <i class="fa-check" style="color:green;">:check:</i> | Read, edit, save, import, and export  |
| Current orchestrator state                          | <i class="fa-check" style="color:green;">:check:</i> | Inspect baseline items                |
| Manual policy state                                 | <i class="fa-check" style="color:green;">:check:</i> | Add forced items and block items      |
| Provider profile state                              | <i class="fa-check" style="color:green;">:check:</i> | Instance-aware                        |
| Pair datasets                                       | <i class="fa-check" style="color:green;">:check:</i> | Inspect and save pair-scoped data     |
| Tracker snapshots                                   | <i class="fa-check" style="color:green;">:check:</i> | Select historical data                |
| Watchlist data                                      | <i class="fa-check" style="color:green;">:check:</i> |                                       |
| History data                                        | <i class="fa-check" style="color:green;">:check:</i> |                                       |
| Ratings data                                        | <i class="fa-check" style="color:green;">:check:</i> |                                       |
| Progress data                                       | <i class="fa-check" style="color:green;">:check:</i> |                                       |
| ZIP export and import                               | <i class="fa-check" style="color:green;">:check:</i> | Tracker editor data exchange          |
| Automatic module checkpoint before provider changes | <i class="fa-check" style="color:green;">:check:</i> | Protects state before remote mutation |

### Maintenance operations

| Operation                                                |                                            Supported | Notes                                                    |
| -------------------------------------------------------- | ---------------------------------------------------: | -------------------------------------------------------- |
| Overview cleanup                                         | <i class="fa-check" style="color:green;">:check:</i> | Runs the standard local cleanup sequence                 |
| Inspect provider cache                                   | <i class="fa-check" style="color:green;">:check:</i> | Shows files and metadata                                 |
| Retry provider items                                     | <i class="fa-check" style="color:green;">:check:</i> | Preserves activity files and the identity cache          |
| Refresh artwork and metadata                             | <i class="fa-check" style="color:green;">:check:</i> | Removes local artwork and metadata cache                 |
| Rebuild sync state                                       | <i class="fa-check" style="color:green;">:check:</i> | Removes saved provider baselines                         |
| Inspect CrossWatch local tracker                         | <i class="fa-check" style="color:green;">:check:</i> | Shows local state and snapshots                          |
| Reset local tracker                                      | <i class="fa-check" style="color:green;">:check:</i> | Can clear tracker state files, snapshots, or both        |
| Clear Recent Scrobbles                                   | <i class="fa-check" style="color:green;">:check:</i> | Removes only local scrobble rows                         |
| Rebuild statistics                                       | <i class="fa-check" style="color:green;">:check:</i> | Clears statistics, reports, and Insights cache           |
| Clear currently playing                                  | <i class="fa-check" style="color:green;">:check:</i> | Removes the local live playback session file             |
| Clear all captures                                       | <i class="fa-check" style="color:green;">:check:</i> | Deletes saved provider captures                          |
| Factory reset                                            | <i class="fa-check" style="color:green;">:check:</i> | Backs up `config.json` first                             |
| Restart CrossWatch                                       | <i class="fa-check" style="color:green;">:check:</i> | Exits for container or process-supervisor restart        |
| Remove reports, caches, and generated state during reset | <i class="fa-check" style="color:green;">:check:</i> | Includes sync reports, tracker data, cache, and TLS data |


# Operations and limits

Security, storage, runtime behavior, limitations, and repository source paths.

This section covers security behavior, storage layout, runtime notes, and the main repository areas behind each system.

{% hint style="info" %}
Use this page when you need exact paths, security behavior, or implementation map pointers.
{% endhint %}

### At a glance

{% columns %}
{% column %}

#### Covers

* secret storage and config safety
* state and backup paths
* runtime logging and telemetry
  {% endcolumn %}

{% column %}

#### Also includes

* key product limits
* source-map pointers into the repo
* related docs for deeper details
  {% endcolumn %}
  {% endcolumns %}

### Jump to related docs

<table data-view="cards"><thead><tr><th>Title</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td>Safe defaults and operating guidance</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/aXxgMcv8AubC0CypFUTO">/spaces/3rh5THg1PdhVsBt3GALo/pages/aXxgMcv8AubC0CypFUTO</a></td></tr><tr><td>Size and provider fit guidance</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/Lws31PpVM04pXySTwH8d">/spaces/3rh5THg1PdhVsBt3GALo/pages/Lws31PpVM04pXySTwH8d</a></td></tr><tr><td>Config file reference</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/wSx6U3V0FNJ3e2pvDywh">/spaces/3rh5THg1PdhVsBt3GALo/pages/wSx6U3V0FNJ3e2pvDywh</a></td></tr><tr><td>Provider pacing and retries</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/uIdojSHyuBmDdMI0yPFg">/spaces/3rh5THg1PdhVsBt3GALo/pages/uIdojSHyuBmDdMI0yPFg</a></td></tr></tbody></table>

### Security and configuration

| Capability                        | Supported | Notes                                                                 |
| --------------------------------- | --------: | --------------------------------------------------------------------- |
| Encrypted stored secrets          |       Yes | Fernet encryption                                                     |
| Generated local master key        |       Yes | Stored as `.cw_master_key` with restrictive permissions when possible |
| External master key               |       Yes | `CW_CONFIG_KEY` or `CROSSWATCH_CONFIG_KEY`                            |
| Secret redaction                  |       Yes | Tokens, API keys, passwords, session IDs, and webhook identifiers     |
| Optional local UI login           |       Yes | Username and password                                                 |
| Password hashing                  |       Yes | PBKDF2 SHA256, 260000 iterations                                      |
| Remember session                  |       Yes | Configurable duration                                                 |
| Multiple login sessions           |       Yes |                                                                       |
| HTTP interface                    |       Yes |                                                                       |
| HTTPS interface                   |       Yes |                                                                       |
| Automatic self-signed certificate |       Yes | Configurable hostname and validity                                    |
| Custom certificate and key        |       Yes |                                                                       |
| Provider SSL verification         |       Yes | Configurable for local server providers                               |
| Atomic configuration writes       |       Yes | Temporary file replacement                                            |
| Configuration backup before reset |       Yes |                                                                       |
| Backup path validation            |       Yes | Restricts access to managed backup paths                              |
| Capture path validation           |       Yes | Restricts access to managed capture paths                             |

### Runtime and storage

| Area                          | Default or support               |
| ----------------------------- | -------------------------------- |
| Persistent configuration root | `/config` in the container image |
| Main configuration            | `/config/config.json`            |
| Main sync state               | `/config/state.json`             |
| Manual policy state           | `/config/state.manual.json`      |
| Provider runtime state        | `/config/.cw_state`              |
| Local CrossWatch tracker      | `/config/.cw_provider`           |
| Local tracker snapshots       | `/config/.cw_provider/snapshots` |
| Captures                      | `/config/snapshots`              |
| Backups                       | `/config/backups`                |
| Sync reports                  | `/config/sync_reports`           |
| Runtime debug logging         | Supported                        |
| HTTP access logging           | Supported                        |
| Sync module debug logging     | Supported                        |
| Provider request rate limits  | Supported                        |
| Provider retry budgets        | Supported                        |
| Usage telemetry               | Enabled by default, configurable |

### Important limitations

{% hint style="warning" %}
Destructive removals stay off by default for a reason. Test first with dry runs.
{% endhint %}

1. There is no general playlist sync feature.
2. Jellyfin and Emby ratings are not currently exposed as sync features.
3. Jellyfin and Emby watchlists are emulated through Favorites, Playlist, or Collections.
4. Jellyfin and Emby Playlist watchlist mode is episode-oriented and cannot represent shows.
5. Tautulli is a read-only history source.
6. AniList only supports anime watchlist and ratings workflows.
7. TMDb only supports watchlist and ratings sync.
8. Trakt, SIMKL, and MDBList do not participate in normal pair-based progress sync, even though they are supported by the Playback Progress Manager.
9. PublicMetaDB writes require TMDb identity, and progress writes require duration.
10. Plex progress removal may be limited by Plex behavior.
11. Destructive removals are globally disabled by default, but a pair can explicitly enable them.
12. Mirror delete mode is destructive and should be used only when the source must be authoritative.
13. Drop guard and mass-deletion settings should be configured deliberately, especially during the first sync.
14. Plex fallback GUID searching increases runtime and resource use and is intended as a temporary recovery option.
15. Provider APIs, account permissions, rate limits, and upstream behavior can still prevent an operation that the CrossWatch adapter implements.
16. PunchPlay support is experimental. Its adapter behavior may change.


# Main dashboard

Overview of the Main page, its actions, layout, and linked dashboard sections.

<figure><img src="/files/Esim1tHUWMS2WlwtMM3X" alt=""><figcaption><p>Main page overview with top actions, live status, widgets, and output.</p></figcaption></figure>

The **Main** page is the operational home for CW.

Use it to start runs, watch status, inspect output, and open the most-used tools.

It is built around three jobs:

1. start and monitor work
2. review live status
3. open deeper tools when something needs attention

### Top actions

The first row contains the main action buttons.

Use them to jump into the most common tasks:

* **Synchronize** — start a sync run
* **View details** — inspect recent sync results
* **Analyzer** — investigate matching and state problems
* **Events** — review searchable sync history and recorded outcomes
* **Exporter** — build export files from CW data

These actions are for running and inspecting work.

Most configuration still lives under **Settings**.

### Main page sections

#### Status dashboard

The status row gives you a fast operational summary.

Unlike **Settings → Overview**, this area focuses on live runtime state.

It covers:

* monitoring status for **Scheduler**, **Watcher**, and **Webhook**
* system status for **Sync pairs**, **CW Health**, and **Updates**

Use [Status dashboard](/crosswatch/main-dashboard/status-dashboard) for the full reference.

#### Output panel

The output area shows live and recent run detail.

Use it to inspect:

* **Sync** output
* **Watcher** output
* **Debug** output

Use [Output panel](/crosswatch/main-dashboard/output-panel) for the full guide.

#### Dashboard widgets

The widget area shows media-focused summaries from existing CW data.

That includes:

* **Watchlist widget**
* **Recent History widget**
* **Latest Ratings widget**
* **Recent Scrobble widget**

Use [Dashboard widgets](/crosswatch/main-dashboard/dashboard-widgets) for widget behavior, requirements, and troubleshooting.

#### Recent Activity

The **Main** page also shows a separate text-based activity list.

This is different from the visual **Recent Scrobble** widget.

Use [Recent Activity](/crosswatch/settings/scrobbler/recent-activity) for that audit trail.

### What belongs on Main

Use **Main** when you want to:

* start a run
* confirm live service state
* inspect recent output
* review dashboard summaries

Use **Settings** when you want to change configuration.

### Related topics

* [Navigation](/crosswatch/main-dashboard/navigation)
* [Status dashboard](/crosswatch/main-dashboard/status-dashboard)
* [Dashboard widgets](/crosswatch/main-dashboard/dashboard-widgets)
* [Watchlist](/crosswatch/main-dashboard/navigation/watchlist)
* [Quick Add](/crosswatch/main-dashboard/navigation/quick-add)
* [Playback](/crosswatch/main-dashboard/navigation/playback)
* [Output panel](/crosswatch/main-dashboard/output-panel)
* [Events](/crosswatch/main-dashboard/tools/events)
* [Recent Activity](/crosswatch/settings/scrobbler/recent-activity)
* [Settings](/crosswatch/settings)


# Navigation

Map the CrossWatch web UI and find the main settings and tools.

Use the UI to connect providers, configure pairs, and run syncs.

Most setup problems come from [**Connections**](/crosswatch/settings/connections), [**Pairs**](/crosswatch/settings/configure-pairs), or [**Metadata / ID Mapping**](/crosswatch/settings/connections/metadata).

{% hint style="success" %}
If you feel lost in the UI, start from **Settings** and **Main**.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### Start here

Use these pages most often:

* [Main dashboard](/crosswatch/main-dashboard) — overview and top actions
* [Status dashboard](/crosswatch/main-dashboard/status-dashboard) — live system status
* [Dashboard widgets](/crosswatch/main-dashboard/dashboard-widgets) — watchlist, history, ratings, and scrobble widgets
* [Watchlist](/crosswatch/main-dashboard/navigation/watchlist) — unified watchlist view
* [Quick Add](/crosswatch/main-dashboard/navigation/quick-add) — manual history, watchlist, and rating actions
* [Playback](/crosswatch/main-dashboard/navigation/playback) — unfinished playback records
* [Output panel](/crosswatch/main-dashboard/output-panel) — Sync, Watcher, and Debug output

<figure><img src="/files/zhmU9eXYsDVYv9VGo3kq" alt=""><figcaption><p>Main navigation with the dashboard, tools, and settings entry points.</p></figcaption></figure>

### Common paths

#### Connect providers

1. Open **Settings → Connections**
2. Connect what you need
3. Click **Save**

#### Create a pair

1. Open **Synchronization**
2. Create one pair first
3. Start with **one-way** and **one feature**

#### Run and check

1. Go to **Main**
2. Run a sync
3. Review the results
4. Fix mismatches in [Analyzer](/crosswatch/main-dashboard/tools/analyzer) or [Editor](/crosswatch/main-dashboard/navigation/editor)

### Saving changes

* Click **Save** to persist changes.
* Active runs use the next saved settings.
* Some changes need a restart.
  {% endtab %}

{% tab title="Power users" %}

### Key settings areas

Most configuration lives under **Settings**.

Use these pages for the deeper controls:

* [Settings](/crosswatch/settings)
* [Connections](/crosswatch/settings/connections)
* [Metadata / ID Mapping](/crosswatch/settings/connections/metadata)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Scheduling](/crosswatch/settings/scheduling)
* [Scrobble](/crosswatch/settings/scrobbler)
* [Recent Activity](/crosswatch/settings/scrobbler/recent-activity)
* [Maintenance](/crosswatch/settings/maintenance)
* [HTTPS/TLS](/crosswatch/advanced-and-api/https-and-tls)
* [Plex SSO](/crosswatch/settings/ui-settings/security/plex-sso)

### Notes

Connected providers appear in **Synchronization** only after they connect.

Most setup issues come from authentication, mapping, or pair rules.

For feature-level behavior, use [Synchronization](/crosswatch/settings/synchronization) and [Tools](/crosswatch/main-dashboard/tools).
{% endtab %}
{% endtabs %}


# Playback

View and manage unfinished playback records across connected providers.

Playback shows unfinished movies and episodes from connected providers.

Use it to review, edit, complete, or remove resume records.

{% hint style="info" %}
Playback is available in **v0.9.23 and later**.
{% endhint %}

<figure><img src="/files/NadQxdWTJiHlftRhbukA" alt=""><figcaption></figcaption></figure>

{% tabs %}
{% tab title="End users" %}

### What it shows

Each card can show:

* the movie or episode
* current progress
* time remaining
* when playback last changed
* provider badges

When **All Providers** is selected, CrossWatch can combine matching records into one card.

### What you can do

Use the toolbar to:

* search
* filter by provider, type, progress, age, or rating
* change sorting

Use the card actions to:

* **Edit Progress**
* **Mark as Watched**
* **Remove Progress**

You can also select multiple cards and run bulk actions.

### Important behavior

* **Edit Progress** accepts `2` through `79` percent.
* Use **Mark as Watched** for completed items.
* **Remove Progress** deletes the resume record only.
* On **Plex**, removing progress can also affect watched state.

### Refresh and results

**Refresh** requests current provider data again.

One slow provider does not block the others.

Playback can still show partial results.

### Playback vs Progress

**Playback** shows the current provider state.

**Progress Sync** stores state only when a pair runs.

Use [Progress](/crosswatch/settings/configure-pairs/features/progress) for pair-based resume syncing.

Use the editor for the last stored sync state.

### Troubleshooting

If nothing appears:

* confirm the provider is connected
* confirm it is enabled in Playback settings
* use **Refresh**

If **Edit Progress** is missing, the record usually cannot be updated or has no known duration.
{% endtab %}

{% tab title="Power users" %}

### Supported providers

Playback supports:

* **Trakt**
* **SIMKL**
* **MDBList**
* **PublicMetaDB**
* **Plex**
* **Emby**
* **Jellyfin**
* **Nuvio**

All supported providers can read playback records.

Write support depends on the provider and record data.

### Combining logic

With **All Providers**, CrossWatch groups matching records when possible.

If no strong media key exists, fallback matching uses title and progress details.

Records can stay separate when identifiers are weak or progress differs too much.

### Settings

Playback settings let you:

* include or exclude provider profiles
* change **Slow provider timeout**

Timeout accepts `3` through `60` seconds.

The default is `12`.

### Advanced notes

* CrossWatch queries enabled providers in parallel.
* Up to six provider requests run at once.
* **Select All Filtered Results** loads up to `250` results.
* Saving Playback settings clears the Playback cache.

<details>

<summary>Config and API details</summary>

Playback settings are stored in `config.json` under `playback_progress`.

Useful endpoints:

```
GET /api/playback_progress/providers
GET /api/playback_progress/settings
GET /api/playback_progress/items
POST /api/playback_progress/settings
POST /api/playback_progress/actions/remove
POST /api/playback_progress/actions/mark_watched
POST /api/playback_progress/actions/update_progress
POST /api/playback_progress/actions/bulk
```

The update action accepts only `2` through `79` percent.

</details>

### Related

* Resume syncing between servers: [Progress](/crosswatch/settings/configure-pairs/features/progress)
* Real-time playback handling: [Scrobble](/crosswatch/settings/scrobbler)
* Main page overview: [Main dashboard](/crosswatch/main-dashboard)
* UI overview: [Navigation](/crosswatch/main-dashboard/navigation)
  {% endtab %}
  {% endtabs %}


# Captures

Create point-in-time captures for restore, compare, and scheduled retention workflows.

Captures are a **rollback tool**.

A capture stores a point-in-time copy of provider data so you can restore it later.

You can also compare captures or keep scheduled backup-style snapshots over time.

Older builds called this feature **Snapshots**.

{% hint style="warning" %}
Captures are **experimental** and will stay experimental.

They are **not** a core CW feature. They’re a nice-to-have service.

Treat captures as best-effort:

* Use with care.
* Validate restores before doing destructive changes.
* Create a real backup first. Use your provider’s native export/backup
  {% endhint %}

<figure><img src="/files/v0xHZfx8POFgrxsiFu8Z" alt=""><figcaption></figcaption></figure>

### Captures are not backups

Captures are **not** the same as a full backup.

They are a best-effort copy of:

* one **provider**
* one **profile**
* one **dataset** (Watchlist, Ratings, History, Progress)

They do **not** include:

* provider credentials or tokens
* CW configuration
* Items outside the scope of the sync adapter
  * For example: **History items without a date won’t be included in a capture, but the process can still clear them.**
  * Plex note: this often means items that are only **Marked Watched** (the Plex “checkmark”) without a usable `viewedAt` / `lastViewedAt`.
    * If you want CW to also scan “Marked Watched” state, set `plex.history.include_marked_watched = true`.
    * This is still best-effort. Items without timestamps can’t be exported into a history capture.
    * More detail: [Adapter: Plex](/crosswatch/settings/synchronization/media-servers/plex).
* everything your provider may store (notes, list metadata, privacy settings, etc.)

{% hint style="info" %}
If you want a real backup, prefer provider-native export/backup features. Captures are the fallback when those are missing.
{% endhint %}

### What captures are good for

Use captures before you:

* run destructive tools (clear watchlist/ratings/history)
* clean up lists in bulk
* migrate accounts

### What a capture contains

A capture is a JSON export of **one provider**, **one profile**, and **one feature**.

You can also capture **All** features together.

It includes:

* provider id (example: `PLEX`, `TRAKT`, `SIMKL`, `ANILIST`)
* provider profile or instance
* feature (`watchlist`, `ratings`, `history`, `progress`, or `all`)
* created timestamp and optional label
* basic stats (counts and media-type breakdown where available)
* the raw items needed to restore back into the same provider

#### Whitelisting impacts captures (media servers)

For **Plex/Jellyfin/Emby**, captures follow your library whitelists.

If you whitelist specific libraries for a feature, the capture includes **only items from those libraries**.

Example:

* History whitelist = only those libraries are exported in a **History** capture.
* Ratings whitelist = only those libraries are exported in a **Ratings** capture.

If you don’t configure whitelisting, captures include **all visible libraries**.

Guide: [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting).

### Where captures are stored

Captures are written to:

`/config/snapshots/YYYY-MM-DD/`

This folder name is legacy. It may still say `snapshots` on disk.

Filenames include a UTC timestamp, provider, feature, and optional label:

`2026-01-27T19-12-45Z__TRAKT__watchlist__before-cleanup.json`

#### “All” captures

If you choose **All**, CW creates:

* one full capture bundle (`__all__`) that references child captures
* one child capture per supported feature

This is useful when you want one complete restore point across multiple features.

It also keeps restore behavior predictable and lets you restore one feature later.

### Create Capture

The Create Capture section lets you create a new capture manually.

#### Main options

* **Provider**: choose the source provider
* **Profile**: choose the provider profile or instance
* **Feature**: choose one feature or **All**
* **Label**: optional custom name for the capture

#### Available actions

* **Create capture**: create the capture immediately
* **Queue for scheduler**: send the current setup into Capture Scheduling

Use **Queue for scheduler** when you want to turn a one-off setup into a recurring automated capture.

{% hint style="info" %}
Use short labels like `before-import` or `after-cleanup`.
{% endhint %}

### Capture Browser

The Capture Browser shows all saved captures in one place.

You can:

* search by provider, feature, label, or path
* filter by provider
* filter by feature
* filter by type:
  * **Manual**: captures created manually
  * **Auto**: captures created by Capture Scheduling

For full captures, the browser can group and show child captures under the main **All** capture.

Click a capture to load it into **Restore** or **Compare**.

Click it again to clear the selection.

### Restore

Restore applies a saved capture back into a provider profile.

#### Restore modes

* **Merge**: add missing data from the capture without wiping the target first
* **Clear restore**: clear the target feature first, then restore the captured data

{% hint style="warning" %}
CW restores what was captured.

The target media server or tracker may still interpret that data differently afterward.
{% endhint %}

{% hint style="warning" %}
Plex has no supported way to **backdate** a play.

If CrossWatch writes **History** into Plex, Plex records `watched_at` as **now**.

This affects history sync and **History** capture restores.

That matters because when you later syncs history back from Plex to a tracker, CrossWatch send those Plex timestamps back to the tracker as if they were the real watch dates. This can overwrite the user’s historical watch timeline.
{% endhint %}

#### Restoring “All” captures

If the selected capture is a bundle (`feature = all`), Restore applies the chosen mode to each supported child feature.

### Compare

Compare lets you select two matching captures and see what changed.

#### Rules

* both captures must belong to the same provider
* you can compare two single-feature captures
* you can compare two **All** capture bundles that contain multiple features

This is useful for checking:

* items added
* items removed
* items updated

Compare is designed to answer: “What changed between capture A and capture B?”

It does **not** answer: “Which provider is correct?” or “What should sync?”

More detail: [Capture Compare](/crosswatch/main-dashboard/navigation/captures/capture-compare) and [Capture Compare (Advanced)](/crosswatch/main-dashboard/navigation/captures/capture-compare-advanced).

### Tools

The Tools area provides cleanup and maintenance actions for provider features.

That includes clearing supported feature data directly from the target provider.

{% hint style="danger" %}
Provider tools write directly to the provider. Use them carefully.
{% endhint %}

Available tools depend on what the selected provider supports:

* Clear watchlist
* Clear ratings
* Clear history
* Clear progress
* Clear all

{% hint style="warning" %}
**Clear ratings** trigger rating-scrobble listeners.

If you enabled rating scrobbling, this can instantly send “unrate” events.

That can wipe ratings on attached providers too (example: Trakt).

Before using **Clear ratings**, disable rating scrobbling first:

* **Settings → Scrobbler → Watcher routes → Plex ratings**
* **Settings → Scrobbler → Webhooks → Ratings**

Setup references:

* [Watcher](/crosswatch/settings/scrobbler/watcher)
* [Webhooks](/crosswatch/settings/scrobbler/webhooks)
  {% endhint %}

If a provider doesn’t support a feature, that tool is disabled.

Typical workflow:

1. Create a capture
2. Run a clear tool
3. Optional: restore a capture

### Scheduler Integration

Captures now work much more closely with scheduling.

You can:

* create a capture manually right now
* queue that setup directly into Capture Scheduling

That means a manual capture setup can become a repeating automated capture schedule without re-entering the provider, profile, and feature.

Related: [Scheduling](/crosswatch/settings/scheduling)

### Scheduled Capture Retention

Capture schedules can manage retention automatically.

Available options:

* **Label template**: define how scheduled captures are named
* **Keep captures for days**: delete captures older than a set age
* **Max captures to keep**: keep only the newest number of captures
* **Cleanup**: enable automatic pruning after each successful scheduled capture

So Captures now support:

* manual restore points
* scheduled recurring backup-style captures
* compare and restore workflows
* automatic retention cleanup for scheduled captures

### Deleting captures

Captures can be deleted from the UI.

* Select a capture
* Click **Delete**
* Confirm

If you delete an **All features** bundle, CW also deletes its child captures.

This only deletes files on disk under `/config/snapshots/...`. It does not touch provider data.

### Notes and limitations

* Providers differ. A restore can be best-effort, depending on provider APIs.
* If a feature/tool is disabled, the provider does not support it (or is not configured).
* Captures are local files. If you move hosts, copy `/config/snapshots` too.
* Don’t forget: this is **not** a backup tool.


# Capture Compare

Run a read-only diff between two same-provider captures to see added, removed, updated, and unchanged items.

Capture Compare is a **read-only diff** between two capture files from the **same provider**.

It helps you answer: “What changed between capture A and capture B?”

It does **not** decide what should sync.

{% hint style="info" %}
Captures are stored under `/config/snapshots/` (legacy folder name). Capture Compare never writes to your providers.
{% endhint %}

### What you can compare

You pick:

* **Capture A** (baseline)
* **Capture B** (the one you’re comparing against)

Both captures should be from the **same provider**.

Capture Compare supports:

* **single-feature captures**
  * Watchlist
  * Ratings
  * History
  * Progress
* **All** capture bundles
  * one set that contains multiple features in one restore point

For the cleanest results, compare like-for-like:

* feature vs the same feature
* **All** bundle vs **All** bundle

### How to run a diff (UI)

{% stepper %}
{% step %}

### Open Compare

Open **Tools → Captures**, then open the **Compare** panel.
{% endstep %}

{% step %}

### Pick two captures

Pick Capture **A** and Capture **B**.

Keep the provider the same.

Keep the feature, or bundle type, the same when you can.
{% endstep %}

{% step %}

### Run Compare

Click **Compare** to load results.

Use the filters to focus on Added/Removed/Updated.
{% endstep %}
{% endstepper %}

### How to read results

Compare groups items into four buckets:

* **Added**: in B, not in A
* **Removed**: in A, not in B
* **Updated**: same key, but fields changed
* **Unchanged**: same key, identical payload

Updated items can be expanded to show field-level changes.

{% hint style="warning" %}
If you diff captures from different features, everything can look “added/removed”. That’s expected.
{% endhint %}

{% hint style="info" %}
Compare is intended for **same-provider diffs**. If you compare different providers, results won’t be meaningful.
{% endhint %}

### Limits and truncation

The UI may cap how many rows it shows per section.

If you expect huge diffs, tighten your comparison first.

Try same provider, same feature or same bundle type, close timestamps, and a short label.

<details>

<summary>API reference (optional)</summary>

#### Endpoint

`GET /api/snapshots/diff`

#### Query params

* `a` (required): capture A path (relative under `/config/snapshots`)
* `b` (required): capture B path (relative under `/config/snapshots`)
* `limit` (optional, default `200`, min `1`, max `2000`): max items returned per section
* `max_changes` (optional, default `25`, min `1`, max `200`): max field-level changes per updated item

</details>

<details>

<summary>How the diff works (high level)</summary>

Compare is intentionally simple.

It loads both capture JSON files, then compares:

* the set of item keys in A vs B
* the full item payload for keys present in both

That produces:

* Added = keys in B but not A
* Removed = keys in A but not B
* Updated = key in both, payload differs
* Unchanged = key in both, payload identical

{% hint style="info" %}
This is deterministic. It does not do ID mapping or fuzzy matching.
{% endhint %}

</details>


# Capture Compare (Advanced)

Advanced capture diff viewer with raw JSON and field-level changes.

Capture Compare (Advanced) is a **read-only diff viewer**.

It’s the “power user” version of [Capture Compare](/crosswatch/main-dashboard/navigation/captures/capture-compare).

Use it when you need raw proof.

{% hint style="info" %}
This tool never writes to providers.

It only reads capture files and shows differences.
{% endhint %}

<figure><img src="/files/CsXxExwg4nvjVDdYYcjD" alt=""><figcaption></figcaption></figure>

### What it’s good for

Use Advanced compare when you want:

* Side-by-side browsing of **File A vs File B**
* Field-level changes for **Updated** items
* Raw JSON for both sides
* Quick copy for keys and external IDs (imdb/tmdb/etc.)

### Open Advanced compare

{% stepper %}
{% step %}

### Open Captures

Open **Tools → Captures**.
{% endstep %}

{% step %}

### Open Compare

Open the **Compare** panel.
{% endstep %}

{% step %}

### Pick File A and File B

Pick two captures from the same provider.

You can compare one feature at a time or an **All** bundle with multiple features.

Keep the feature, or bundle type, the same when you can.
{% endstep %}

{% step %}

### Open Advanced

Open the advanced compare viewer.

Use **Refresh** if the files changed on disk.
{% endstep %}
{% endstepper %}

### How to use the viewer

#### Header summary

The header shows provider + feature and four counters:

* `+` Added
* `-` Removed
* `~` Updated
* `=` Unchanged

#### Filters

Use filters to cut noise:

* **Search**: title, key, and common external IDs
* **Status chips**: Added, Removed, Updated, Unchanged
* **Type**: Movies / Shows / Seasons / Episodes
* **Sort**: status / title / key
* **Changed only**: hide unchanged items

#### Two-pane list (A vs B)

The top section shows two lists:

* Left = items present in **File A**
* Right = items present in **File B**

Selecting an item syncs selection across panes.

#### Item inspector

The bottom section shows details for the selected key:

* Status + change count (for Updated)
* Copy buttons (key, JSON A, JSON B)
* For Updated: a list of changed fields (JSON path + values)
* Two raw records side-by-side (A and B)

{% hint style="info" %}
For **Added** items, File A is empty.

For **Removed** items, File B is empty.
{% endhint %}

<details>

<summary>Viewer tips</summary>

* Use the splitters to resize the A/B panes and the detail area.
* Scroll is kept in sync in the detail panes when possible.
* Clicking an ID chip copies its value.
* Clipboard can be blocked by browser security policies.

</details>

### Status semantics

Same rules as standard compare:

* **Added**: present in B, missing in A
* **Removed**: present in A, missing in B
* **Updated**: present in both, payload differs
* **Unchanged**: present in both, payload matches

### Performance and limits

Advanced compare can load a lot of data.

If it feels slow, reduce the scope:

* Compare captures close in time.
* Compare the same provider and the same feature or bundle type.
* Hide **Unchanged**.

<details>

<summary>API reference (optional)</summary>

#### Endpoint

`GET /api/snapshots/diff/extended`

#### Notes

The API path still uses `snapshots`.

The UI calls them captures.

</details>


# Editor

Review stored provider data, manage manual policy and send selected items to configured providers.

The **Editor** manages stored provider data and manual policy.

Use it to:

* review stored provider state
* correct titles, years, media types, IDs, ratings, watched dates, and progress
* add manual items or block future syncs
* import provider state or send selected rows directly
* edit supported playlist endpoints

{% tabs %}
{% tab title="End users" %}

### Quick start

{% stepper %}
{% step %}

### 1. Pick a source

Choose one of the following:

* **Current State** — provider baselines with manual policy
* **Manual Overrides** — saved manual additions, corrections, and blocks
* **Local Tracker** — CrossWatch Local Tracker data
* **Playlist Endpoint** — a configured provider playlist
  {% endstep %}

{% step %}

### 2. Pick a dataset

Choose **Watchlist**, **History**, **Ratings**, or **Progress**.

Playlist Endpoint uses its configured playlist data.
{% endstep %}

{% step %}

### 3. Pick a scope

For **Current State** and **Manual Overrides**, select a provider and profile.

For **Local Tracker**, select a profile and workspace.

For **Playlist Endpoint**, select the configured endpoint.
{% endstep %}

{% step %}

### 4. Edit or select rows

Edit, add, block, or select rows.
{% endstep %}

{% step %}

### 5. Save or send

Use **Save changes** to persist changes for the current source.

Use **Send to** to write selected rows to other provider profiles.
{% endstep %}
{% endstepper %}

### Sources

#### Current State

Current State shows the latest stored baseline for a provider, together with manual additions, corrections, and blocks.

Current State does not query the provider live when you open the view. It shows the state stored by the most recent relevant sync or provider state import.

#### What you can do

1. Review a provider baseline
2. Block baseline rows
3. Add manual rows
4. Replace incorrect baseline items with manual corrections
5. Edit existing manual corrections
6. Inspect advanced item fields
7. Send selected rows to another provider
8. Import fresh provider state, when import is enabled

#### Saving Current State

Baseline rows are not directly rewritten by a normal Editor save.

When you save:

1. Deleted baseline rows become blocks
2. Added rows become manual additions
3. Replaced rows become manual corrections
4. Policy is written to `/config/state.manual.json`
5. Policy is also mirrored into the manual section of `/config/state.json`

This separation allows policy to survive a later rebuild of provider baselines, provided that `state.manual.json` is retained.

#### Manual Overrides

Manual Overrides shows only the policy stored for the selected provider, profile, and dataset.

It does not show the complete provider baseline.

Use this source when you want to review or manage:

1. Manual additions
2. Manual metadata corrections
3. Item blocks

Manual Overrides is useful when a provider baseline contains thousands of rows and you only want to inspect the changes introduced through Editor.

#### Saving Manual Overrides

Save writes the displayed additions and blocks to:

`/config/state.manual.json`

When `state.json` exists, the policy is also mirrored into its manual section.

Removing a manual addition removes that correction from policy. It does not remove an item from the connected provider.

#### Local Tracker

Local Tracker shows data stored by the CrossWatch provider module.

Local Tracker data is grouped by Profile and Workspace. A workspace normally represents a sync pair that uses Local Tracker.

Supported datasets depend on the files available in that workspace and can include:

Watchlist

History

Ratings

Progress

#### What Local Tracker changes mean

Local Tracker rows are read from the local tracker files.

Editor does not overwrite those tracker files when you click Save.

Instead, Save stores corrections and blocks as policy for the CrossWatch provider. These corrections are applied when Local Tracker is used as the source of a future one way sync.

Connected provider accounts are not changed by a Local Tracker save.

#### Local Tracker limitations

Local Tracker policy affects future one way syncs from Local Tracker.

It does not rewrite an existing provider account immediately.

Use Send to when selected Local Tracker rows should be written to a provider immediately.

#### Playlist Endpoint

Playlist Endpoint connects Editor to playlists configured on the Playlists page.

Unlike Current State, Manual Overrides, and Local Tracker, this source works directly with the provider playlist.

The available actions depend on the provider endpoint capabilities.

An endpoint may support:

1. Reading items
2. Adding items
3. Removing items
4. Reordering items

Smart or read only playlists cannot be edited.

#### Saving a Playlist Endpoint

Editor compares the displayed rows with the current provider playlist.

Depending on endpoint support, Save can:

1. Add new items
2. Remove missing items
3. Apply a new order

Provider warnings are shown where relevant. Some providers may require confirmation before removals.

Playlist changes are applied directly to the connected provider.

### Send to (selective sync)

Send to lets you send selected rows directly to one or more provider profiles.

This is a selective write operation. It does not run a complete sync pair and does not create a permanent sync rule.<br>

|                                  |                                  |
| -------------------------------- | -------------------------------- |
| ![](/files/93f4E1GYXtxySoIUTt4r) | ![](/files/7jwUIUiWotG0kOlnJzW8) |

#### Supported datasets

Send to supports:

Watchlist

History

Ratings

Progress

Playlist rows can also be selected and sent as Watchlist data.

#### How to use Send to

1. Open any Editor source
2. Select one or more active rows
3. Click Send to in the selection toolbar
4. Select one or more provider profiles
5. Click Send selected data
6. Review the result for each target

Only active rows are sent. Blocked or deleted rows are excluded.

#### Available targets

Editor only shows provider profiles that:

1. Are configured
2. Support writing the selected dataset
3. Are not the provider profile currently being viewed

Multiple profiles can be selected in one operation.

The selected targets are remembered separately for each dataset in the browser.

#### Data requirements

Watchlist rows require enough metadata to identify the item.

History rows require a watched timestamp.

Ratings rows require a rating from 1 to 10.

Progress rows require a stored progress value.

Items without sufficient data are reported as invalid and are not sent.

External IDs provide the most reliable matching. IMDb, TMDB, TVDB, Trakt, SIMKL, AniList, and MAL IDs may be used when supported by the source and target.

Title and year matching may be available for some providers, but it is less reliable.

#### What Send to changes

Send to calls the target provider write operation directly.

It only performs additions or updates supported by that dataset. It does not remove items from the destination.

Each selected target is processed independently. One target can succeed while another returns unresolved items or errors.

The result includes:

Sent

Attempted

Skipped

Unresolved

Errors

Invalid

Confirmed items are also reflected in the target profile baseline inside Current State. This keeps the stored state aligned with the direct send where confirmation is available.

Send to does not save unsaved Editor changes automatically. Save corrections first when the edited values must become part of policy.

### Import provider state

Current State can import live datasets from a configured provider profile.

The Import provider state panel is only shown when provider state import is enabled. This is normally controlled through debug or developer import settings.

Import can be used without creating a sync pair first.

#### Supported imports

Depending on provider support, you can import:

Watchlist

History

Ratings

Progress

Only configured providers and supported datasets are shown.

#### Import modes

Replace baseline, replaces the selected provider baseline for that dataset with the imported result

Merge, keeps existing baseline rows and adds or updates rows returned by the provider

#### How to import

1. Set Source to Current State
2. Expand Import provider state
3. Pick a Provider
4. Pick a Profile
5. Select one or more datasets
6. Choose Replace baseline or Merge
7. Click Import

Import reads from the provider API and writes the result into the appropriate baseline in `state.json`.

It does not modify the provider account.

Existing manual additions and blocks remain in `state.manual.json`.

Provider specific library filters and configured scopes are passed through the provider adapter. The exact filtering behavior therefore depends on the provider and dataset.

Large imports can take time. The Editor shows import progress while the request is running.

### Blocking items

Blocks are policy rules.

A block applies to a specific:

Provider

Provider profile

Dataset

Item key

When an item is blocked, it is excluded when that provider profile is used as the source of a future sync for that dataset.

A block does not delete the item from the provider.

#### Block one item

1. Set Source to Current State or Local Tracker
2. Pick a dataset
3. Pick the provider, profile, or workspace
4. Find a baseline row
5. Click the block icon
6. Click Save changes

Click the block icon again before saving to unblock the row.

#### Review blocked items

Enable the Blocked type chip to show only blocked baseline rows.

Manual Overrides also shows stored block entries, including blocks for rows that are no longer present in the current baseline.

### Block rules

Block rules provide a faster way to block or unblock all baseline rows of one media type.

The panel is available in policy sources for History, Ratings, and Progress.

#### Use Block rules

1. Open Current State, Manual Overrides, or Local Tracker
2. Select History, Ratings, or Progress
3. Expand Block rules
4. Choose a media type
5. Click Block all or Unblock all
6. Click Save changes

Block all only affects baseline rows of the selected type.

Manual additions are not converted into blocks by this operation.

### Manual additions

Manual additions represent items that should be treated as present in a source, even when they are not part of its current baseline.

#### Add an item

1. Click Add row
2. Enter or generate a unique Key
3. Select the media Type
4. Enter a Title and Year where available
5. Add external IDs
6. Enter the dataset specific value
7. Click Save changes

For History, enter a watched date.

For Ratings, enter a rating.

For Progress, enter playback progress.

A nonempty row must have a Key before it can be saved.

External IDs are strongly recommended. Rows based only on title and year may not be writable to every provider.

### Correcting baseline items

Baseline rows in policy sources are protected from direct editing.

This prevents accidental modification of the stored provider snapshot.

To correct a baseline item, use the replace or search action.

Editor then:

1. Creates a manual correction
2. Blocks the original key where required
3. Stores the correction in policy

This allows the original provider baseline to remain intact while future syncs use the corrected item.

### Advanced fields

Policy sources include an Advanced fields action.

This view shows:

1. The row source
2. The dataset
3. The canonical key
4. The row origin
5. The complete stored item data

Use Advanced fields to inspect IDs, show IDs, episode coordinates, timestamps, provider metadata, and other fields not displayed in the main table.

### Editing dataset values

#### Watchlist

Watchlist rows represent item presence.

Supported media types are normally Movie, Show, and Anime.

#### History

History rows include a watched timestamp.

The Extra column opens the watched date editor.

Season and episode rows may also include series metadata and episode coordinates.

#### Ratings

Ratings rows include a rating from 1 to 10.

The Extra column opens the rating editor.

#### Progress

Progress rows contain stored playback position or percentage data.

The Extra column opens the progress editor.

Progress shown in Current State or Local Tracker is stored state. It is not a live provider query.

Use Import provider state or run the relevant Progress sync to refresh it.

The Playback Progress Manager uses live provider queries and may therefore show different values.

### Episodes and seasons

Episode rows can display the series title together with season and episode coordinates, for example:

`Series Name, S01E02`

Search accepts formats such as:

`S01`

`S01E02`

Episode and season corrections can use the replacement workflow to select the correct item or coordinates.

### Filtering and sorting

#### Filter

The filter searches across:

Title

Series title

Season and episode code

Key

Media type

Year

External IDs

Multiple words can be entered. All entered terms must match the row search data.

#### Type filters

Available type filters include:

Movies

Shows

Anime

Seasons

Episodes

Blocked

Watchlist only uses Movie, Show, and Anime.

At least one regular media type remains enabled.

#### Sorting

Rows can be sorted by:

Key

Type

Title

Extra

Extra sorting depends on the dataset.

For Ratings, Extra sorts by rating.

For History, Extra sorts by watched date.

### Selection

Rows can be selected individually or by selecting the current page.

The table shows up to 100 rows per page.

Selection enables bulk actions:

Send to

Block or Delete

Unblock or Restore

Clear selection

Select page only selects rows on the current page.

Selection is cleared after a completed Send to operation.

### Saving

Save changes behaves differently for each source.

#### Current State

Saves manual additions, corrections, and blocks as policy.

Provider baselines are not overwritten.

#### Manual Overrides

Saves only the displayed policy additions and blocks.

Removing a row removes that manual override.

#### Local Tracker

Saves corrections and blocks as CrossWatch provider policy.

Local tracker data files are not overwritten.

#### Playlist Endpoint

Applies supported additions, removals, and reordering directly to the provider playlist.

### Policy backup and restore

Policy Backup protects manual additions, corrections, and blocks.

#### Export

Policy Export downloads:

`crosswatch-state-policy.json`

The export includes policy for provider profiles and Local Tracker profiles stored in `state.manual.json`.

It does not contain complete provider baselines.

#### Import

Policy Import merges the imported policy into the existing policy by default.

Imported policy is also mirrored into `state.json` when that file exists.

Use a policy backup when replacing `state.json`, moving CrossWatch to another installation, or testing large policy changes.

Importing policy does not modify connected providers directly.
{% endtab %}

{% tab title="Power users" %}

### File locations

#### Current State baseline

`/config/state.json`

Contains stored provider baselines, runtime state, and mirrored manual policy.

#### Manual policy

`/config/state.manual.json`

Contains persistent manual additions, corrections, and blocks.

#### Local Tracker

The root is configured through:

`crosswatch.root_dir`

The default Docker location is:

`/config/.cw_provider`

Additional Local Tracker profiles are stored below:

`/config/.cw_provider/profiles/`

Editor discovers Local Tracker workspaces from the available Watchlist, History, Ratings, and Progress files.

Editor reads these files but saves corrections separately as policy.

### Current State storage

Current State reads its provider baseline from:

`/config/state.json`

It reads manual policy from:

`/config/state.manual.json`

### Editor internals

Editor combines several data layers.

For Current State:

Provider baseline from `state.json`, plus policy from `state.manual.json`

For Manual Overrides:

Policy additions and blocks only

For Local Tracker:

Local tracker rows, plus CrossWatch provider policy

For Playlist Endpoint:

A live playlist snapshot from the provider

Normal Save updates policy for Current State, Manual Overrides, and Local Tracker.

Selective Send calls the provider add operation directly for each selected target.

Provider state import reads live provider indexes and stores them as baselines.

Playlist Endpoint Save uses provider playlist add, remove, and reorder operations according to endpoint capabilities.
{% endtab %}
{% endtabs %}

### Related topics

* [Quick Add](/crosswatch/main-dashboard/navigation/quick-add)
* [Watchlist](/crosswatch/main-dashboard/navigation/watchlist)
* [Main dashboard](/crosswatch/main-dashboard)


# Quick Add

Manually send history, ratings, and watchlist actions to one or more providers for titles watched outside your usual media server flow.

Quick Add sends manual actions for a movie or show.

Use it for watches that happened outside your normal server flow.

{% hint style="info" %}
Quick Add is available in **v0.9.18 and later**.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### What it does

Quick Add can send:

* **History**
* **Watchlist**
* **Rating**

You can send one action or combine them.

Typical use cases:

* cinema visits
* travel or offline viewing
* missed scrobbles

### How to use it

{% stepper %}
{% step %}

### 1. Find the title

Choose **Movies** or **Shows**.

Search TMDb and pick the right result.
{% endstep %}

{% step %}

### 2. Choose the action

Select **History**, **Watchlist**, **Rating**, or a mix.
{% endstep %}

{% step %}

### 3. Fill the extra fields

If you use **History**, choose when you watched it.

If you use **Rating**, select a score.
{% endstep %}

{% step %}

### 4. Pick providers

Select the providers that should receive the action.
{% endstep %}

{% step %}

### 5. Send it

Click **Send to providers**.
{% endstep %}
{% endstepper %}

### Notes

* **Desktop** uses the side launcher.
* **Mobile** uses the floating button.
* **Send To** starts collapsed on mobile.

### Limits

* Quick Add depends on TMDb search.
* Provider support differs by action.
* Rating needs a valid score before sending.
  {% endtab %}

{% tab title="Power users" %}

### Action behavior

* **History** marks the item as watched.
* **Watchlist** adds it to provider watchlists.
* **Rating** sends the selected score.

Actions are checked per provider.

Unsupported actions are skipped for that provider.

Nuvio supports **Watchlist** and **History** actions. It does not support ratings.

### Provider selection

Only configured providers are shown.

Multi-instance providers are supported.

CrossWatch can restore your last provider selection.

You can also use:

* **Use last**
* **Select all**
* **Clear**

### Watched date options

For **History**, you can use:

* **Today**
* **Release date**
* **Choose date**

**Release date** uses TMDb metadata.

It does not represent when you personally watched the item.
{% endtab %}
{% endtabs %}

### Related topics

* [Main dashboard](/crosswatch/main-dashboard)
* [Watchlist](/crosswatch/main-dashboard/navigation/watchlist)
* [Dashboard widgets](/crosswatch/main-dashboard/dashboard-widgets)


# Watchlist

Unified watchlist view across configured providers, with filters, local hiding, and provider-aware deletes.

The **Watchlist** page shows one unified view of watchlist items across CrossWatch.

It combines matching items from configured providers into one interface.

Use it to review, filter, compare, hide, and remove watchlist items.

{% hint style="info" %}
This page shows data from the most recent CrossWatch sync.

It does not continuously query every provider while open.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### What it does

The page merges matching entries from your configured providers.

If the same item exists on Plex, Trakt, and SIMKL, it appears once.

The page can show:

* total and visible item counts
* last synchronized time
* poster, title, release, genre, and type
* which providers currently have the item

### Coverage Pulse

**Coverage Pulse** shows how well the **currently visible** watchlist is represented across active providers.

It is not a sync-health check.

Use it to see:

* how complete the current view is
* which provider has the best coverage
* how filters change the coverage picture

#### Sync density

**Sync density** is the main percentage.

Quick reading:

* `100%` — every visible item exists on every active provider
* `75%` — visible items exist on about three quarters of provider slots
* `50%` — visible items exist on about half of provider slots

Lower does not always mean sync failed.

Provider differences and filters can reduce it.

#### Provider cards

Each active provider gets a card with:

* provider name or logo
* visible items found on that provider
* provider coverage percentage
* instance names, when available

If you filter to one provider, that provider usually shows `100%`.

The other cards then show how many of those same items exist elsewhere.

### Views and filters

Use **List view** to compare metadata and provider presence.

Use **Poster view** for a more visual scan.

Main filters:

* **Search**
* **Type**
* **Provider**

Select **More** for:

* release state
* genre
* hidden items

Changing filters also recalculates **Coverage Pulse**.

### Delete vs Hide local

Use **Delete** to remove selected items from one provider or all supported providers.

Use **Hide local** to hide items only in the current browser.

**Hide local** does not remove anything from external providers.

### Refresh

**Refresh** reloads the stored CrossWatch watchlist.

It does not always start a new provider sync.

If you need newer provider data, run a sync from [Main dashboard](/crosswatch/main-dashboard).

### Limits

Keep these in mind:

* provider badges can be outdated until the next sync
* missing TMDb setup means less artwork and metadata
* local hidden items are browser-specific

For pair behavior, see [Watchlist feature](/crosswatch/settings/configure-pairs/features/watchlist).
{% endtab %}

{% tab title="Power users" %}

### Data source

The page reads unified watchlist items from `/api/watchlist/`.

CrossWatch loads up to `5,000` items per response.

Matching provider rows are normalized into one item with provider membership attached.

Only active configured providers appear in the matrix and in **Coverage Pulse**.

### Sync density formula

**Sync density** is calculated from the filtered visible result:

```
Provider memberships for all visible items
──────────────────────────────────────────── × 100
Visible items × Active providers
```

The result is rounded to the nearest whole percent.

Example:

```
100 visible items
3 active providers
240 memberships

240 / 300 × 100 = 80%
```

### Provider coverage cards

Each provider card uses:

```
Visible items present on provider
────────────────────────────────── × 100
Total visible items
```

Instance labels are collected from visible items only.

The default instance is shown first.

### Filters and local state

Coverage recalculates after changes to:

* search
* type
* provider
* release state
* genre
* hidden-item visibility

Local UI preferences include:

* list or poster view
* poster size
* advanced-filter visibility
* release and genre filters
* overlay preference
* hidden-item visibility

### Metadata and limits

TMDb can enrich visible items with artwork, dates, genres, overview, runtime, and trailers.

Coverage Pulse does **not** prove:

* provider connectivity
* last sync success
* correct matching
* write support

Use [Output panel](/crosswatch/main-dashboard/output-panel) and [Analyzer](/crosswatch/main-dashboard/tools/analyzer) for troubleshooting.
{% endtab %}
{% endtabs %}

### Related topics

* [Navigation](/crosswatch/main-dashboard/navigation)
* [Main dashboard](/crosswatch/main-dashboard)
* [Dashboard widgets](/crosswatch/main-dashboard/dashboard-widgets)
* [Watchlist feature](/crosswatch/settings/configure-pairs/features/watchlist)


# Insights

Filter the Insights dashboard by feature and provider profile.

Insights is the dashboard that summarizes your data across providers.<br>

<figure><img src="/files/AyoL8UZECR5tfzZk9QMh" alt=""><figcaption></figcaption></figure>

It can show:

* watchlist
* ratings
* history
* progress

<figure><img src="/files/qXWRv4UObCmQAFk2QUHf" alt=""><figcaption></figcaption></figure>

### Why this exists

If you use [Profiles](/crosswatch/settings/connections/profiles), Insights totals get noisy.

Insights settings let you:

* hide features you don’t care about
* include/exclude specific provider profiles from totals

### Where settings are stored

Settings are stored **in your browser**, not on the server.

* Key: `insights.settings.v1`

If you open CrossWatch in a different browser/device, you’ll get defaults again.

### How profile filtering works

Profiles are called **Profiles** in the UI.

* If you never changed profile selections, Insights includes **all profiles**.
* Once you make a selection for a provider, Insights aggregates **only those profiles**.

Newly discovered profiles are auto-added to your selection.

### Reset

Use the Insights settings modal to reset.

That clears the local settings and reloads defaults.

{% hint style="info" %}
This does not change sync pairs or Watcher routes.

It only changes what the dashboard displays.
{% endhint %}

### Related

* Profiles: [Profiles](/crosswatch/settings/connections/profiles)
* API payload used by Insights: [API](/crosswatch/advanced-and-api/api-reference)


# Status dashboard

Live status row on the Main page for monitoring, provider health, and updates.

<figure><img src="/files/1sLBBbrcf6fkKi6HwIc6" alt=""><figcaption><p>Status row on the Main page for monitoring, health, and update checks.</p></figcaption></figure>

This page covers the status row on **Main**.

Use [Main dashboard](/crosswatch/main-dashboard) for the full page overview.

Unlike **Settings → Overview**, this row focuses on live operational state.

It gives you a fast operational view of CW.

It groups status into two areas:

1. **Monitoring status** — **Scheduler**, **Watcher**, and **Webhook**
2. **System status** — **Sync pairs**, **CW Health**, and **Updates**

Select a status item to open its settings page or run a fresh check.

### Status indicators

The dashboard uses color and motion to show state.

| Indicator          | Meaning                                                  |
| ------------------ | -------------------------------------------------------- |
| Green              | Enabled and working normally                             |
| Red                | Unavailable, misconfigured, or needs attention           |
| Grey               | Disabled, still checking, or status unavailable          |
| Animated indicator | Activity is currently in progress                        |
| Progress ring      | Active playback progress from **Watcher** or **Webhook** |

Hover over a status item to see more detail.

Tooltips can show the next scheduler run, connected providers, active playback streams, or installed version.

### Monitoring status

#### Scheduler

The **Scheduler** runs synchronization automatically from the saved schedule.

It only appears when standard or advanced scheduling is enabled.

#### States

| State              | Meaning                        |
| ------------------ | ------------------------------ |
| `scheduled`        | Standard scheduling is enabled |
| `advanced`         | Advanced scheduling is enabled |
| Animated indicator | A scheduled task is running    |
| `next <time>`      | Next expected run time         |

CW refreshes scheduler status every `30` seconds while scheduling is enabled.

#### Advanced Scheduler badges

Advanced schedules can show extra badges:

| Badge                 | Meaning                             |
| --------------------- | ----------------------------------- |
| `C1`, `C2`, and so on | Number of enabled capture schedules |
| `E1`, `E2`, and so on | Number of enabled event triggers    |

A capture schedule is counted only when it is active and has a provider, feature, and execution time.

An event trigger is counted only when it is active, uses the `sync_pair` action, and includes both a sync pair and route identifier.

Selecting this status opens [Scheduling](/crosswatch/settings/scheduling).

#### Watcher

The **Watcher** polls your media servers for active playback.

Use it when scrobbling is enabled and **Watcher** is the selected source.

#### Information shown

During playback, the status can show:

1. media title
2. playback progress
3. playback state
4. active stream count
5. provider and provider instance

When more than one stream is active, the status shows a badge.

The tooltip shows stream-level detail.

#### States

| State           | Meaning                                                    |
| --------------- | ---------------------------------------------------------- |
| Active playback | The service is running and at least one stream is active   |
| Running or idle | The service is available, but no active playback was found |
| `unavailable`   | The service did not respond successfully                   |

CW waits `35` seconds before marking **Watcher** unavailable.

This avoids brief network blips causing false alarms.

When **Webhook** support is enabled, playback detail refreshes every `5` seconds.

Otherwise, the normal refresh interval is `15` seconds.

Selecting this status opens [Watcher](/crosswatch/settings/scrobbler/watcher).

#### Webhook

The **Webhook** status shows playback events received from supported media servers.

Unlike **Watcher**, webhook mode receives pushed playback events instead of polling for them.

#### Information shown

During playback, the status can show:

1. media title
2. playback progress
3. playback state
4. simultaneous webhook stream count
5. source provider

When no playback is active, the status stays available and shows that the webhook listener is ready.

Webhook streams are tracked separately from Watcher streams.

CW can identify sources such as Plex, Emby, and Jellyfin webhooks.

{% hint style="warning" %}
A listening webhook state does not prove end-to-end delivery.

It only shows that CW is configured and ready to receive events.
{% endhint %}

Selecting this status opens [Webhooks](/crosswatch/settings/scrobbler/webhooks).

### Multiple playback streams

**Watcher** and **Webhook** can each report more than one active stream.

When that happens:

1. the total stream count appears as a badge
2. the visible stream rotates every `30` seconds
3. the tooltip lists all known streams

Watcher and Webhook rotate independently.

### System status

#### Sync pairs

The **Sync pairs** status shows whether CW has usable synchronization routes.

CW counts configured pairs and checks how many are enabled.

A pair is treated as active unless its `enabled` value is set to `false`.

**States**

| State       | Meaning                           |
| ----------- | --------------------------------- |
| `none`      | No sync pairs are configured      |
| `available` | Pairs exist, but all are disabled |
| `active`    | At least one pair is enabled      |

The tooltip shows the total pair count and the enabled pair count.

Selecting this status opens [Configure Pairs](/crosswatch/settings/configure-pairs).

#### CW Health

**CW Health** shows whether configured providers are reachable.

It checks providers that are both configured and enabled.

It then compares them with the latest connection results.

**States**

| State       | Meaning                                          |
| ----------- | ------------------------------------------------ |
| `checking`  | Provider status is not ready yet                 |
| `none`      | No providers are configured                      |
| `healthy`   | All configured providers are connected           |
| `attention` | One or more configured providers are unavailable |

When attention is required, the tooltip can show:

1. connected provider count
2. total configured provider count
3. unavailable provider names

Selecting **CW Health** starts a new provider connection check.

**What it validates**

This check confirms that CW can reach the configured provider APIs with the current credentials and settings.

It does not guarantee:

1. every item will match cleanly
2. every provider supports every feature
3. a sync run will complete without item-level errors
4. remote APIs are free from rate limits or temporary failures

Use [Output panel](/crosswatch/main-dashboard/output-panel) and [Health](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/wb7Ll0gOfQcVcNezKShx) when you need deeper detail.

#### Updates

The **Updates** status compares the installed CW version with the latest GitHub release.

Version strings are normalized before comparison.

That means values like `v1.2.3` and `1.2.3` compare correctly.

**States**

| State       | Meaning                                                   |
| ----------- | --------------------------------------------------------- |
| `checking`  | Update check is still running                             |
| `available` | A newer public release is available                       |
| `current`   | Installed version matches the latest release              |
| `ahead`     | Installed version is newer than the latest public release |
| `unknown`   | Release check could not be completed                      |

The tooltip can show:

1. installed version
2. latest available version
3. whether the build is ahead of public release
4. whether the release check failed

An `ahead` status is normal for development builds or images that contain unreleased changes.

Selecting **Updates** starts a new update check.

### Automatic refresh

The dashboard refreshes when CW expects state to have changed.

Typical triggers include:

1. opening the **Main** page
2. returning focus to the browser tab
3. saving configuration
4. changing sync pairs
5. authentication changes
6. provider status updates
7. update check completion
8. manual scheduler or Watcher refresh

CW pauses unnecessary polling while the browser tab is hidden.

Polling resumes when the page becomes visible again.

### Limits

The status dashboard is a summary.

It does not replace run results or logs.

Use **View details** for per-run results.

Use the application logs when you need to inspect:

* authentication failures
* provider API errors
* item matching failures
* webhook delivery problems
* Watcher connectivity problems
* scheduler execution errors

### Related topics

* [Main dashboard](/crosswatch/main-dashboard)
* [Navigation](/crosswatch/main-dashboard/navigation)
* [Output panel](/crosswatch/main-dashboard/output-panel)
* [Dashboard widgets](/crosswatch/main-dashboard/dashboard-widgets)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Scheduling](/crosswatch/settings/scheduling)
* [Watcher](/crosswatch/settings/scrobbler/watcher)
* [Webhooks](/crosswatch/settings/scrobbler/webhooks)
* [Health](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/wb7Ll0gOfQcVcNezKShx)


# Dashboard widgets

Media widgets on the Main page

{% hint style="warning" %}
All dashboard widgets require a configured TMDb metadata key.

Without it, CW hides these widgets on the **Main** page. Set it up in [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata).
{% endhint %}

<figure><img src="/files/a4udXTmrLzizpDvscbMN" alt=""><figcaption><p>Media widgets on the Main page, including watchlist, history, ratings, and scrobble summaries.</p></figcaption></figure>

{% tabs %}
{% tab title="End users" %}

### Available widgets

#### Watchlist

Shows recently synchronized watchlist items.

Select a media item to open its details. Select **View all** to open the complete [Watchlist](/crosswatch/main-dashboard/navigation/watchlist) page.

#### Recent History

Shows the latest watched movies, shows, seasons, and episodes found in provider state and the CrossWatch tracker.

Episode entries include the season and episode number when available.

#### Latest Ratings

Shows the latest ratings found in provider state and the CrossWatch tracker.

Each entry can include the rating value, media type, timestamp, and provider sources.

#### Recent Scrobble

Shows recent successful scrobble activity.

Entries can show the source media server, destination trackers, playback event, and the route used by CrossWatch.

#### Recent Progress

Shows recent resume position synchronization activity.

Entries include the synchronized progress percentage and the provider sources involved.

This widget only contains data when Progress is enabled on a supported media server pair.

#### Recent Playlists

Shows recent playlist synchronization activity.

Entries can include the mapping profile, action, status, and the number of added or removed items.

This widget only contains data after a playlist mapping has run.

### Opening media details

Select a media item in Watchlist, Recent History, Latest Ratings, Recent Scrobble, or Recent Progress to open the media details card.

Depending on the available metadata, the card can show artwork, an overview, runtime, certification, rating information, provider sources, progress, and links to TMDb or IMDb.

### Customizing widgets

Move the pointer over a widget to reveal its layout controls.

#### Change the order

Use the drag control to move a widget to another position.

Widgets automatically adjust to the available dashboard width.

#### Change the size

Use the size control to switch between compact and horizontal mode.

Compact widgets share the available dashboard columns.

Horizontal widgets use the full dashboard width.

#### Change the view

Compact widgets can use a grid or poster view.

Horizontal widgets can use media cards or a poster carousel.

Recent Playlists uses the poster carousel when displayed horizontally.

#### Hide a widget

Use the visibility control to hide a widget from the current layout.

#### Reset widget (unhide)

To restore hidden widgets, open the dashboard tools in the Sync Hub header and select the visibility button. This restores hidden Sync Hub blocks and dashboard widgets.

<figure><img src="/files/wEACf7quBe3iqrYIcYW1" alt=""><figcaption></figcaption></figure>

### Enabling widgets

Open **Settings → User Interface → Dashboard** to enable or disable individual widgets.

The following widgets are enabled by default:

* Watchlist
* Recent History
* Latest Ratings
* Recent Scrobble

The following widgets are disabled by default:

* Recent Progress
* Recent Playlists

A widget disabled in Settings cannot be restored using the dashboard visibility button. Enable it again under User Interface settings.

These settings only control the interface. They do not enable or disable the associated synchronization feature.

### Layout storage

Widget order, size, view, and locally hidden state are stored in the browser.

This allows different browsers and devices to use different dashboard layouts.

The Show or Hide settings under User Interface are stored in the CrossWatch configuration and apply to every browser using that CrossWatch installation.

Clearing browser storage resets the local widget layout.

### Refreshing widgets

CrossWatch refreshes dashboard data after relevant activity, such as a completed synchronization, a stopped scrobble, a manual history change, or a watchlist update.

Select the refresh button on any widget to force a reload of all currently active dashboard widgets.

The widgets are previews and only display the most recent available entries.

### Empty widgets

An empty widget is shown in a reduced state.

An empty widget does not necessarily indicate a problem. It can mean that the feature is not configured, no successful activity has been recorded yet, or no suitable data is available.

### Metadata and artwork

Configure the TMDb metadata provider to enable posters, backdrops, media details, and external media links.

The Watchlist widget requires TMDb metadata. Other widgets can still show activity without TMDb, but artwork and media details may be limited.
{% endtab %}

{% tab title="Power users" %}

### Power users

Most users should manage dashboard widgets through the interface. The options below are intended for manual configuration, API use, troubleshooting, and browser specific layouts.

#### Configuration keys

Widget availability is controlled under the `ui` section of `config.json`.

```json
{
  "ui": {
    "show_watchlist_preview": true,
    "show_recent_history_widget": true,
    "show_latest_ratings_widget": true,
    "show_recent_scrobble_widget": true,
    "show_recent_progress_widget": false,
    "show_recent_playlists_widget": false
  }
}
```

The default values are:

`show_watchlist_preview`, `true`

`show_recent_history_widget`, `true`

`show_latest_ratings_widget`, `true`

`show_recent_scrobble_widget`, `true`

`show_recent_progress_widget`, `false`

`show_recent_playlists_widget`, `false`

These settings determine whether a widget is available on the Main dashboard.

They are different from hiding a widget through the dashboard layout controls. A widget disabled in `config.json` is not loaded and cannot be restored using the visibility control.

Manual configuration changes should normally be followed by a CrossWatch restart or configuration reload.

#### Browser layout storage

Widget layout preferences are stored in browser local storage under:

```
cw.dashboardWidgets.layout.v3
```

The stored object contains one entry for each widget:

```json
{
  "watchlist": {
    "order": 0,
    "size": "large",
    "view": "icon",
    "horizontalView": "media",
    "hidden": false
  },
  "history": {
    "order": 1,
    "size": "small",
    "view": "grid",
    "horizontalView": "media",
    "hidden": false
  }
}
```

Supported properties:

`order`, determines the widget position

`size`, accepts `small` or `large`

`view`, accepts `grid` or `icon` for compact widgets

`horizontalView`, accepts `media` or `poster` for horizontal widgets

`hidden`, controls local visibility

The Recent Playlists widget always uses its poster layout when displayed horizontally.

Layout preferences are local to the browser. They are not written to `config.json` and are not synchronized between devices.

Clearing browser storage removes the custom layout and restores the default widget order, sizes, views, and visibility.

Editing the local storage value manually is possible, but invalid values are normalized or replaced with defaults when the dashboard loads.

#### Dashboard widgets API

Recent dashboard activity is available through:

```http
GET /api/dashboard/widgets
```

Supported query parameters:

`include`, comma separated widget names

`history_limit`, maximum Recent History entries

`ratings_limit`, maximum Latest Ratings entries

`scrobble_limit`, maximum Recent Scrobble entries

`progress_limit`, maximum Recent Progress entries

`playlists_limit`, maximum Recent Playlists entries

Each limit accepts a value from `1` through `24`.

Example:

```http
GET /api/dashboard/widgets?include=ratings,progress,playlists&ratings_limit=12&progress_limit=8&playlists_limit=8
```

Possible values for `include` are:

`history`

`ratings`

`scrobble`

`progress`

`playlists`

Only requested widgets are built and returned. This can reduce unnecessary provider state and activity processing.

Example response structure:

```json
{
  "ok": true,
  "latest_ratings": {
    "items": [],
    "total": 0
  },
  "recent_progress": {
    "items": [],
    "total": 0
  },
  "recent_playlists": {
    "items": [],
    "total": 0
  }
}
```

The Watchlist widget is loaded through the separate Watchlist preview flow and is not included in `/api/dashboard/widgets`.

#### Data sources

Recent History combines provider state with CrossWatch tracker history.

Latest Ratings combines provider state with CrossWatch tracker ratings.

Recent Scrobble uses successful scrobble and history synchronization events.

Recent Progress combines provider progress state with CrossWatch tracker progress.

Recent Playlists uses playlist synchronization activity.

Duplicate media rows can be merged when CrossWatch can identify them as the same item. Available provider IDs, titles, years, episode numbers, timestamps, and mapping aliases can affect this process.

#### Refresh behaviour

The dashboard keeps recently rendered widget data while navigating between pages.

Widgets are marked for refresh after relevant activity, including synchronization completion, stopped scrobbles, manual history changes, Watchlist changes, and settings changes.

A manual refresh reloads all currently active dashboard widgets, not only the widget whose refresh button was selected.

Hidden or disabled widgets are excluded from the dashboard API request.
{% endtab %}
{% endtabs %}


# Output panel

Inspect Sync, Watcher, and Debug output from the Main page.

The **Output panel** on **Main** shows live text output from CrossWatch.

Use it to inspect sync runs, Watcher activity, and diagnostic messages.

<figure><img src="/files/ynmop8cFqV19btoYgjUc" alt=""><figcaption><p>Output tabs on the Main page for sync runs, watcher activity, and debug logs.</p></figcaption></figure>

### Tabs

The Output panel includes three tabs:

* **Sync** — output from sync runs
* **Watcher** — output from Watcher and scrobble activity
* **Debug** — combined diagnostic output for troubleshooting

### Understand sync results

Use [Sync statuses](/crosswatch/main-dashboard/output-panel/sync-statuses) to read counters and outcomes such as:

* `added`, `removed`, and `updated`
* `skipped`, `blocked`, and `unresolved`
* `attempted`, `pairs`, and compact summaries like `+8 / -2 / ~4`

### Debug output

The **Debug** tab shows diagnostic messages from CrossWatch and its provider modules.

It also includes sync and watcher-related output in one place.

Provider logs still appear in the Docker console.

CrossWatch also copies provider log output into the **Debug** tab.

### What the Debug tab shows

Each entry includes a timestamp and a source tag.

The output can show:

* which provider operation ran
* which sync stage was reached
* provider responses and status codes
* skipped or unsupported operations
* rate limits and authentication problems
* matching and metadata problems
* exceptions and processing failures

### How the Debug tab works

CrossWatch keeps recent diagnostic messages in an in-memory buffer.

When you open **Debug**, CrossWatch loads the buffered messages first.

New messages then stream into the tab in real time.

The Debug tab:

* follows new messages automatically
* reconnects if the stream drops
* lets you copy the current tab with **Copy**

Use [Events](/crosswatch/main-dashboard/tools/events) after a sync finishes.

Events keeps searchable run history, related attempts, and recorded reasons.

{% hint style="warning" %}
The log buffer lives only in memory.

Restarting CrossWatch clears it.

Collect the **Debug** output before you restart the container.
{% endhint %}

### Collect logs for support

When you report a sync or provider problem:

{% stepper %}
{% step %}

### Reproduce the problem

Run the sync or playback flow that shows the issue.
{% endstep %}

{% step %}

### Open the Output panel

Go to **Main**.

Open **View details**.

Select **Debug**.
{% endstep %}

{% step %}

### Wait for the run to finish

Let the affected sync complete or fail.
{% endstep %}

{% step %}

### Copy the output

Click **Copy**.

Paste the full output into the support request.

Prefer a code block or a text file.
{% endstep %}
{% endstepper %}

The **Copy** button copies the currently selected tab.

If you are on **Debug**, it copies **Debug** output.

### What to include with the logs

Also include:

* your CrossWatch version
* the providers involved
* what you expected to happen
* what actually happened

### When Debug output is required

Use **Debug** output for support requests involving:

* sync failures
* missing items
* provider errors
* unexpected sync results

Without the relevant Debug output, it is often not possible to identify the failing provider request, matching decision, or sync stage.

{% hint style="warning" %}
CrossWatch redacts known credential fields.

Review the output before sharing it.

Logs can still contain media titles, account names, server addresses, and provider identifiers.
{% endhint %}

### Related topics

* [Main dashboard](/crosswatch/main-dashboard)
* [Status dashboard](/crosswatch/main-dashboard/status-dashboard)
* [Navigation](/crosswatch/main-dashboard/navigation)
* [Maintenance](/crosswatch/settings/maintenance)
* [Events](/crosswatch/main-dashboard/tools/events)
* [Synchronization](/crosswatch/settings/synchronization)
* [Watcher](/crosswatch/settings/scrobbler/watcher)


# Sync statuses

Meaning of added, removed, updated, skipped, blocked, unresolved, errors, and sync summary counters.

CrossWatch reports sync statuses during planning, apply, and summary output.

Use this page to read a run quickly.

{% hint style="info" %}
Read a sync in this order:

1. **Plan** or **attempted** tells you what CrossWatch tried.
2. **Added**, **removed**, and **updated** tell you what changed.
3. **Skipped**, **blocked**, **unresolved**, and **errors** explain why the rest did not change.
   {% endhint %}

### Quick status guide

| Status       | What it means                                        | Usually needs action? |
| ------------ | ---------------------------------------------------- | --------------------- |
| `added`      | CW created the item on the destination.              | No                    |
| `removed`    | CW deleted the item on the destination.              | No                    |
| `updated`    | CW changed an existing destination item.             | No                    |
| `skipped`    | CW intentionally did nothing.                        | Usually no            |
| `blocked`    | CW filtered the item before writing.                 | Sometimes             |
| `unresolved` | CW could not match the item on the destination.      | Yes                   |
| `errors`     | The operation failed or could not complete reliably. | Yes                   |

### Changed statuses

#### Added

The item was added to the destination provider.

Examples:

* a movie was added to a watchlist
* a rating was created
* a history entry was written
* playback progress was created

An item counts as **added** only after the destination confirms the write.

#### Removed

The item was removed from the destination provider.

Examples:

* a movie was removed from a watchlist
* a rating was deleted
* a playlist item was removed

After a removal, CW can store a tombstone.

That helps stop the same item from being re-added immediately in a later run.

#### Updated

The destination already had the item, but CW changed it.

Examples:

* a rating value changed
* playback progress changed
* an existing history record was updated
* playlist metadata changed

### Run counters

#### Attempted

**Attempted** is the number of items CW sent into a provider operation.

It does not guarantee that the destination changed.

Some attempted items can later end up as skipped, blocked, unresolved, or errors.

Example:

```
10 attempted
8 added
1 skipped
1 unresolved
```

#### Confirmed

**Confirmed** means the destination provider reported a successful result.

In normal summaries, confirmed items are reflected as **added**, **removed**, or **updated**.

Confirmed is more reliable than attempted.

#### Plan

The **plan** is the set of changes CW calculated before writing.

It can include:

* items to add
* items to remove
* items to update

Example:

```
Adding 12, removing 3, updating 4
```

The final result can differ from the plan.

Items can still be skipped, blocked, unresolved, or fail during apply.

#### Pairs

**Pairs** is the number of configured provider routes processed in the run.

A pair is a sync path between two providers.

Examples:

```
PLEX -> TRAKT
PLEX <-> TRAKT
```

Pairs does not mean media item count.

### No-change outcomes

#### Skipped

CW intentionally did not apply the item.

Common reasons:

* the destination already has the same value
* there is no effective change
* the destination does not support that item
* required information is missing
* sync rules exclude the item
* the provider returned a no-change result

Skipped is usually not an error.

#### No change

**No change** means CW checked the item and the destination already matched the expected state.

No provider change was needed.

Depending on the provider and feature, CrossWatch can report this as **skipped**, **no change**, or **no operation**.

#### No operation

A **no operation** result, sometimes shown as `NOOP`, means the provider accepted the request but changed nothing.

Examples:

* deleting an item that was already absent
* adding an item that already existed
* updating an item with the same value

A no-operation result is normally not an error.

### Problem outcomes

#### Unresolved

CW could not match the item to a valid destination item.

Common reasons:

* no supported provider ID was available
* the destination provider could not find the item
* title and year did not produce a safe match
* the media type did not match
* the provider was temporarily unavailable
* the provider returned incomplete metadata

Unresolved usually means you should inspect IDs, metadata, or provider state.

Use [Analyzer](/crosswatch/main-dashboard/tools/analyzer) when the same item keeps returning as unresolved.

<details>

<summary>Advanced: unresolved state files</summary>

CW can store unresolved items under `/config/.cw_state/` so later runs can avoid retrying the same bad write immediately.

A pending file can look like this:

```
/config/.cw_state/<provider>_<feature>.unresolved.pending.<scope>.json
```

An unresolved record can include a canonical key, title, year, media type, provider IDs, failure reason, and timestamp.

</details>

#### Blocked

CW excluded the item before it sent the write to the destination provider.

Blocked does not mean the destination rejected it.

It means CW filtered it first.

Common sources:

* a tombstone from an earlier removal
* a previous unresolved item
* an internal blackbox entry
* cross-feature unresolved blocking

<details>

<summary>Advanced: why items get blocked</summary>

**Previously removed**

The item matches a tombstone from an earlier removal.

This helps prevent immediate re-add loops in two-way sync.

**Previously unresolved**

The item failed earlier and is temporarily held back.

This reduces repeated retries for the same bad match.

**Blackbox**

The item is in an internal blocklist because repeated processing is known to fail or behave badly.

**Cross-feature blocking**

An unresolved item from one feature can block the same canonical item in another feature.

Example: a watchlist unresolved item can later block a progress add.

</details>

#### Errors

An **error** means the operation failed or could not complete reliably.

Common causes:

* provider API errors
* authentication failures
* rate limits
* network failures
* invalid provider responses
* unexpected data formats
* internal processing exceptions

Errors are different from unresolved items.

**Unresolved** means CW processed the item but could not match it safely.

**Errors** means the write or provider interaction itself failed.

### How to read the final summary

CW can show a compact summary like this:

```
+8 / -2 / ~4
pairs=1
skipped=3
unresolved=1
errors=0
blocked=2
```

The symbols mean:

| Symbol | Meaning |
| ------ | ------- |
| `+`    | Added   |
| `-`    | Removed |
| `~`    | Updated |

The counters add context:

| Counter      | Meaning                                   |
| ------------ | ----------------------------------------- |
| `pairs`      | Provider pairs processed                  |
| `skipped`    | Items intentionally not changed           |
| `unresolved` | Items that could not be matched           |
| `errors`     | Operations that failed                    |
| `blocked`    | Items filtered before provider processing |

### Example interpretation

```
15 attempted
10 added
2 skipped
1 unresolved
2 blocked
0 errors
```

This means:

* CW tried to process 15 items.
* 10 were confirmed as added.
* 2 needed no effective change, or were intentionally skipped.
* 1 could not be matched safely on the destination.
* 2 were filtered before any provider write.
* no provider or processing errors occurred.

### When to act

* Mostly **added**, **removed**, or **updated** — the run behaved normally.
* Mostly **skipped** or **no change** — the destination was already aligned.
* Repeated **unresolved** — fix IDs, title/year quality, or matching inputs.
* Repeated **blocked** — inspect tombstones, unresolved state, or blocklist behavior.
* Any **errors** — inspect **Debug** output and provider health first.

### Related topics

* [Output panel](/crosswatch/main-dashboard/output-panel)
* [Analyzer](/crosswatch/main-dashboard/tools/analyzer)
* [Metadata / ID Mapping](/crosswatch/settings/connections/metadata)
* [Tombstones](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/AbAHapoU4fVH9SPBWr36)
* [Unresolved](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/KnMPtx1Y6mb3cXWb3723)


# Tools

Debug matching, edit local state, and export CSVs.

Tools are optional UI pages.

Use them for debugging and local data workflows.

Most setups only need [Configure Pairs](/crosswatch/settings/configure-pairs) and [Scheduling](/crosswatch/settings/scheduling).

{% hint style="info" %}
Use tools after a run exposes a problem or before a risky change.
{% endhint %}

### Available tools

<table data-view="cards"><thead><tr><th>Tool</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td>Analyzer</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/urf7i5pr8Dd67jaRYgsQ">/spaces/3rh5THg1PdhVsBt3GALo/pages/urf7i5pr8Dd67jaRYgsQ</a></td></tr><tr><td>Events</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/0FlaZT5iA0GzYOrVViEP">/spaces/3rh5THg1PdhVsBt3GALo/pages/0FlaZT5iA0GzYOrVViEP</a></td></tr><tr><td>Exporter</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/3etr8jiXzeY3oHBt6Zka">/spaces/3rh5THg1PdhVsBt3GALo/pages/3etr8jiXzeY3oHBt6Zka</a></td></tr><tr><td>Editor</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/jnc3CxKIomZJwjuz5RlN">/spaces/3rh5THg1PdhVsBt3GALo/pages/jnc3CxKIomZJwjuz5RlN</a></td></tr><tr><td>Captures</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/qAmxDUAX1DagwLqV0c5P">/spaces/3rh5THg1PdhVsBt3GALo/pages/qAmxDUAX1DagwLqV0c5P</a></td></tr></tbody></table>

### What tools touch

Tools mainly work from local files created by runs.

* Runtime state: `/config/state.json`
* Manual policy (blocks, manual adds): `/config/state.manual.json`
* Provider captures: `/config/snapshots/...`
* CrossWatch tracker files (used for local backup workflows)

Provider writes depend on the tool:

* **Analyzer**: no provider writes.
* **Exporter**: no provider writes.
* **Editor**: edits local state and policy.
  * **Import datasets** reads live provider APIs to populate `state.json`.
* **Captures**: restore and clear actions write to the selected provider dataset.

{% hint style="warning" %}
Back up `/config/state.json` and `/config/state.manual.json` before risky edits.

Create a capture before destructive provider actions.
{% endhint %}

### Pick the right tool

Use **Analyzer** when items didn’t sync.

* Typical: missing IDs, missing peers, bad matches.

Use **Events** when you need the history of a sync or item operation.

* Typical: failed writes, unresolved items, repeated attempts.

Use **Exporter** when you want CSV files for another service.

* It converts local runtime state to import-friendly CSVs.

Use **Editor** when you need to change local state or apply manual policy.

* Typical: block an item, fix a local row, seed data before your first sync.

Use **Captures** when you want a rollback point.

* Typical: before cleanup, before switching providers, before bulk list edits.

### Related topics

* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Scheduling](/crosswatch/settings/scheduling)
* [Maintenance](/crosswatch/settings/maintenance)


# Analyzer

Inspect sync gaps, blocked items, scope exclusions, weak identifiers, provider limits, and local state diagnostics.

Inspect sync gaps, blocked items, scope exclusions, weak identifiers, provider limits, and local state diagnostics.

The Sync Analyzer compares the current CrossWatch state for one or more selected sync pairs.

Use it to determine:

* Which items are missing from a destination
* Which route is affected
* Why an item may not have synchronized
* Whether an item is manually blocked
* Whether pair settings excluded an item
* Whether provider limits prevented an addition
* Whether local state or metadata contains inconsistencies

The Analyzer helps identify the cause of a sync problem.

It does not automatically correct metadata stored in Plex, Jellyfin, Emby, Trakt, or another provider.

### Requirements

Before using the Analyzer:

* Run the affected sync pair at least once
* Confirm that the pair is enabled
* Confirm that the required feature is enabled

Analyzer reads the current local provider and sync state generated by CrossWatch.

When no scoped state is available, run a sync for the selected pair and reopen Analyzer.

Optional metadata integrations can improve the quality of IDs and matching elsewhere in CrossWatch, but they are not required to open Analyzer.

### Open Analyzer

Open the Sync Analyzer from the CrossWatch interface.

The Analyzer loads:

* Configured sync pairs
* Current provider state
* Pair routes and enabled features
* Missing peer results
* Manual block state
* Pair scope exclusions
* Normalization findings
* Provider and state diagnostics

The initial analysis can take longer when the local state contains many items.

### Analyzer layout

The current Analyzer contains:

* View selector
* Sync pair selector
* Analysis status
* Provider item counts
* Result summary
* Search
* Show IDs control
* Result table
* Resizable detail panel
* Analyze button

### Views

#### Needs attention

Needs attention is the default view.

It contains items that are missing, unresolved, risky, or otherwise require review for at least one selected route.

Typical results include:

* An item exists at the source but is missing at a destination
* One destination is missing an item while another destination is aligned
* A provider limit prevented an item from being added
* IDs are incomplete or inconsistent
* A route has a matching or normalization problem

The Analyzer compares selected routes independently.

An item being present at one destination does not hide a gap at another destination.

#### All scoped items

All scoped items displays the current provider state for the selected routes.

It includes both healthy and affected items.

Use this view to:

* Confirm that an item is present in local state
* Inspect IDs for an item without an active issue
* Compare healthy and missing items
* Review the full scope used by the selected pairs

Results are loaded in pages of up to 250 items.

Use **Previous** and **Next** to move between pages.

### Sync pair selection

The **Sync pairs** section controls which routes are analyzed.

You can select one or more pairs.

Analyzer only includes enabled pairs and enabled features.

Supported analyzed features include:

* History
* Watchlist
* Ratings
* Progress

For a one-way pair, Analyzer checks the configured source-to-destination route.

For a two-way pair, Analyzer checks both directions.

Provider profiles are shown with their instance name when a non-default profile is used.

For example:

`PLEX@Family → TRAKT@Primary`

### Analysis status

The sidebar displays three status counters.

#### Issues

Issues is the number of missing peer results found for the selected routes.

The counter can also show totals by feature:

* `H`, History
* `W`, Watchlist
* `R`, Ratings
* `P`, Progress

#### System

System is the number of background diagnostics reported by the Analyzer.

System findings can describe state, cache, metadata, provider, or integrity conditions.

A System finding does not always mean that an item is currently unsynchronized.

It can still explain why a route is unstable or why matching is unreliable.

#### Blocked

Blocked is the number of scoped items currently covered by manual block rules.

Blocked items are identified separately from normal missing peer issues.

### Summary counters

The result header displays the current analysis totals.

#### Scoped

Scoped is the total number of items in the current Analyzer view for the selected pairs.

In **Needs attention**, this is the number of issue items.

In **All scoped items**, this is the total number of provider state items in scope.

#### Visible

Visible is the number of rows currently displayed after applying:

* The selected view
* The selected page
* Pair scope
* Search text

#### Issues

Issues is the total number of missing peer results for the selected routes.

#### System

System is the number of background diagnostic findings.

#### Analysis time

When available, Analyzer also displays the server analysis time in milliseconds.

### Result table

The result table contains:

* Provider
* Feature
* Title
* Type

Select a column heading to sort the table.

Selecting the same heading again reverses the sort direction.

The table supports sorting by:

* Provider
* Feature
* Title
* Type

Episode titles are displayed with their series and episode number.

Example:

`Series Name, S02E05`

Season entries are displayed with their series and season number.

Example:

`Series Name, S02`

### Search

Use **Search** to filter the rows on the current page.

Search checks:

* Provider
* Feature
* Title
* Display title
* Year
* Media type

Multiple search words must all be present in the row data.

Search does not query connected providers.

It only filters the data already loaded into Analyzer.

### Show IDs

Select **Show IDs** to display known item identifiers below each title.

Supported fields can include:

* IMDb
* TMDb
* TVDb
* MAL
* AniList
* Trakt
* Plex
* Simkl
* Emby
* MDBList
* PublicMetaDB

Missing or incorrect IDs can cause matching failures, especially when providers use different primary identifiers.

### Result indicators

#### Missing indicator

An issue row displays an indicator when the item is missing at another provider.

Hover over the indicator to see:

* The missing destination
* The first available reason

When several destinations are selected, the result can identify more than one missing target.

#### Blocked indicator

A blocked label means the item is covered by a manual block rule.

Manual blocks are loaded from the [Editor](/crosswatch/main-dashboard/navigation/editor) state for the same provider and feature.

Blocked items are counted separately and are not treated as normal missing peer issues.

### Detail panel

Select a result row to open its details.

The detail panel can contain:

* Item title and year
* Missing destination
* Analyzer reason
* Blocked status
* Known IDs
* Provider limit information
* History normalization findings
* Pair scope exclusions
* System findings for the selected item
* Other system findings

The divider between the result table and detail panel can be dragged to resize both areas.

The selected size is stored in the browser.

### Issue reasons

Analyzer can provide a reason from the destination or matching analysis.

Possible reasons include:

* Missing at the destination
* Item could not be resolved
* Weak or missing IDs
* Target lookup failed
* Item was excluded by pair scope
* Provider account limit reached
* Manual blocking

Suggestions and reason messages are diagnostic hints.

Verify the item in the source and destination before changing metadata or removing data.

### Provider limits

Analyzer can detect supported provider account limits from current provider status.

For example, when a Trakt account reaches a Watchlist or Collection limit, the detail panel can show:

* The affected provider
* The affected feature
* Current usage
* Account limit
* Number of affected Analyzer items
* The last known limit error

When a provider limit has been reached, correcting IDs will not allow additional items to be added until capacity is available.

Remove items from the destination or change the provider account plan before rerunning the pair.

### Scope exclusions

Analyzer can report items excluded by pair configuration.

Scope exclusions can be caused by:

* Disabled media types
* Feature type restrictions
* Pair direction
* Disabled features
* Other pair-specific scope settings

The detail panel can show:

* Source
* Destination
* Feature
* Number of excluded items per media type
* Allowed media types

An excluded item is not necessarily a sync failure.

It can be working as configured.

### System findings

System findings are background diagnostics produced during analysis.

They can include conditions involving:

* State files
* Provider cache
* Unresolved item records
* Blackbox state
* Tombstones
* Flapping protection
* Watermarks
* Provider modules
* Missing identifiers
* Duplicate or conflicting identifiers
* History show normalization
* Metadata consistency
* Provider validation

Some findings include affected item lists and internal state values.

System findings are divided into:

* Findings related to the selected item
* Other findings from the current analysis

The related findings section opens automatically for the selected result.

Other findings remain collapsed until opened.

### History normalization

Analyzer can report History show normalization findings.

These findings describe cases where show, season, and episode state required normalization or contains inconsistent structure.

Use these findings when:

* Episode History does not align
* A show appears under inconsistent keys
* Provider History contains mixed show and episode data
* Matching succeeds for a show but fails for its episodes

Normalization findings do not always indicate an active missing item.

They provide context for History matching and state structure.

### Edit Manual IDs

The detail panel allows known identifiers to be edited for the selected local Analyzer item.

Use this when the current state contains weak, missing, or incorrect IDs.

The editor supports the Analyzer ID fields shown for the item.

#### Save behaviour

Saving Manual IDs:

* Sends the provider, feature, item key, and ID values to the Analyzer patch endpoint
* Removes empty ID values
* Allows the item key to be rebuilt from the new IDs
* Does not automatically merge identifiers from peer items
* Runs Analyzer again after saving
* Selects the updated item when its key changes

The change updates CrossWatch local runtime state.

It does not directly edit metadata in Plex, Jellyfin, Emby, Trakt, or another connected provider.

A later provider refresh or sync can replace local state when the source provider still reports different IDs.

#### Reset behaviour

Reset restores the fields in the editor to the values that were present when the detail panel was opened.

It does not undo an ID patch that has already been saved.

### Where to fix an issue

The preferred fix depends on where the incorrect data originates.

#### Media server to tracker

For pairs such as Plex, Jellyfin, or Emby to Trakt:

1. Correct the match or metadata in the media server.
2. Refresh the item metadata when required.
3. Run the pair again.
4. Reopen Analyzer.
5. Confirm that the issue has disappeared.

Correcting the source is preferable to maintaining a local Analyzer patch.

#### Tracker to media server

When the tracker is the source:

1. Verify the item in the tracker.
2. Remove an incorrect or duplicate tracker item when necessary.
3. Confirm that the destination can resolve the media.
4. Run the pair again.

Analyzer does not directly edit tracker data.

#### Local state only

Use **Edit Manual IDs** when:

* You need to confirm that stronger IDs resolve the issue
* The provider temporarily reports incomplete IDs
* You are diagnosing a local state mismatch

After testing, correct the metadata at the source so the fix survives future state refreshes.

### Recommended workflow

{% stepper %}
{% step %}

### 1. Run the affected pair

Run a normal sync or Dry Run so the current state is available.
{% endstep %}

{% step %}

### 2. Open Analyzer

Allow the initial state load and analysis to complete.
{% endstep %}

{% step %}

### 3. Select the affected pair

Avoid analyzing unrelated pairs while investigating one route.
{% endstep %}

{% step %}

### 4. Start with Needs attention

Review the issue total and feature breakdown.
{% endstep %}

{% step %}

### 5. Search for the item

Search by title, year, provider, feature, or type.
{% endstep %}

{% step %}

### 6. Select the result

Review the missing destination, reason, IDs, scope exclusions, and related System findings.
{% endstep %}

{% step %}

### 7. Check Blocked and provider limits

Confirm that the item is not blocked and that the destination can accept more items.
{% endstep %}

{% step %}

### 8. Correct the source

Fix metadata, matching, account capacity, or pair configuration.
{% endstep %}

{% step %}

### 9. Run the pair again

Use Dry Run first when the change could affect many items.
{% endstep %}

{% step %}

### 10. Analyze again

Select **Analyze** or reopen the Analyzer.

Confirm that the issue count decreases and that the item is aligned.
{% endstep %}
{% endstepper %}

### Analyze button

Select **Analyze** to rerun the current comparison.

Analyzer refreshes:

* Active pair routes
* Missing peer problems
* Manual block state
* System findings
* Scope exclusions
* Provider limit information

The current search and selected view remain available where possible.

Analyzer requests can run for up to `120` seconds before the interface aborts them.

### Performance and pagination

Analyzer separates issue results from the complete state browser.

Needs attention loads the current issue set and displays it in pages.

All scoped items requests provider state pages from the server.

The page size is `250` items.

Large installations should select only the pairs required for the investigation.

This reduces analysis scope and makes the result easier to interpret.

### Troubleshooting

#### No scoped state yet

Run the selected pair and reopen Analyzer.

Confirm that the pair and required feature are enabled.

#### Scoped is higher than expected

Check:

* Selected pairs
* Pair direction
* Two-way mode
* Enabled features
* Pair media type settings

Switch to one affected pair during troubleshooting.

#### Visible is lower than expected

Clear the search field.

Check the current page and selected view.

Visible only counts rows displayed on the current page after filtering.

#### An item is not listed under Needs attention

Open **All scoped items** and search for it.

The item may be healthy, blocked, excluded by pair settings, or outside the selected routes.

#### Blocked count is unexpected

Open [Editor](/crosswatch/main-dashboard/navigation/editor) and review manual blocks for the affected provider and feature.

Analyzer loads manual block rules from local Editor state.

#### IDs disappear after saving

The source provider may still report the old identifiers.

Correct the metadata at the provider and rerun the pair.

Manual ID changes affect local runtime state and are not written back to the provider.

#### No reason is displayed

Not every missing peer result includes a detailed destination message.

Review the item IDs, System findings, provider logs, and pair configuration.

#### The same issue returns after sync

The underlying source metadata, provider limit, manual rule, or pair scope is still unchanged.

Fix the original cause rather than repeatedly patching local IDs.

#### Analyzer reports no issues but sync still looks wrong

Check:

* The correct pair is selected
* The feature is enabled
* The expected direction is active
* The item is not excluded by type settings
* The item is not manually blocked
* The sync run completed successfully

Then inspect System findings and the [Output panel](/crosswatch/main-dashboard/output-panel).

Use [Events](/crosswatch/main-dashboard/tools/events) to review what happened during the affected sync.

#### Analysis fails or times out

Reduce the selected pair scope and run Analyzer again.

Check the [Output panel](/crosswatch/main-dashboard/output-panel) for provider, state, or metadata errors.

### UI reference

#### View selector

Switches between:

* Needs attention
* All scoped items

#### Sync pairs

Selects the routes included in the comparison.

#### Analysis status

Displays:

* Issues
* System findings
* Blocked items
* Provider item counts

#### Analyze

Reruns the current analysis.

#### Search

Filters the current result page.

#### Show IDs

Shows or hides item identifiers.

#### Result table

Displays provider, feature, title, and media type.

#### Detail panel

Displays item status, IDs, reasons, exclusions, limits, and diagnostics.

#### Resizable divider

Changes the height of the result and detail panels.

### Related topics

* [Editor](/crosswatch/main-dashboard/navigation/editor)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting)
* [Metadata / ID Mapping](/crosswatch/settings/connections/metadata)
* [Output panel](/crosswatch/main-dashboard/output-panel)
* [Events](/crosswatch/main-dashboard/tools/events)
* [Maintenance](/crosswatch/settings/maintenance)
* [Global settings](/crosswatch/settings/configure-pairs/global-settings)


# Events

Search sync history and diagnose failed, unresolved, or blackboxed operations.

Events is the searchable history and diagnostics viewer for CrossWatch sync activity.

Use it to understand what CrossWatch attempted, what succeeded, what failed, and why.

{% hint style="info" %}
Events is diagnostic only. It does not change provider data, repair items, or retry operations.
{% endhint %}

### When to use Events

Use Events to:

* Confirm that a sync started and completed.
* Investigate failed, unresolved, or blackboxed operations.
* Trace an item across runs and provider routes.
* Compare a historical event with current CrossWatch state.
* Copy diagnostic data for a support request.

Use [Analyzer](/crosswatch/main-dashboard/tools/analyzer) to compare current provider baselines.

<figure><img src="/files/snI40AIyUkkwnMet1Dln" alt=""><figcaption></figcaption></figure>

### Open Events

Open the CrossWatch Main dashboard, then select **Events**.

The window has two panels:

* The event list.
* Details for the selected thread or event.

Drag the divider to resize the panels. The browser remembers the selected size.

### How Events works

CrossWatch records structured sync activity in a local SQLite archive.

It correlates related records into readable run and item threads. Opening a thread also loads current configuration and local state.

Historical timelines remain unchanged. Current context is calculated when you open a thread.

This means a historical **Unresolved** thread can show no current unresolved state.

### Choose a view

#### Grouped

**Grouped** is the default view. It combines related records into threads.

A thread can represent a sync run, item operation, provider health result, or planning sequence.

Sync runs appear as parent threads. Problem items appear below their run when available.

Each row can show:

* Status and readable summary.
* Event count, feature, route, and latest event time.
* Whether you acknowledged the record.

Select the arrow next to a run to show or hide its problem items.

#### Raw events

Select **Raw events** to view each archived record separately.

Use Raw events when you need the exact event order, a specific event type, technical identifiers, or the original structured payload.

Records can include sync starts and finishes, provider health checks, write attempts and outcomes, unresolved changes, blackbox decisions, tombstones, and rating updates.

Switching views never changes the archive.

### Find the right record

#### Visibility

The visibility selector controls acknowledged records.

* **Open** shows records you have not acknowledged.
* **Acknowledged** shows reviewed records.
* **All** shows both.

Open does not mean currently failing. A completed thread remains open until you acknowledge it.

Acknowledging only organizes the Events archive. It never retries, resolves, or changes an item.

#### Search and filters

Search by title, item key, failure reason, reason code, or run information.

Search also checks related archived events. It runs shortly after you stop typing.

In Grouped view, use the outcome filter:

* **Successful** shows completed or resolved threads.
* **Problems** shows failed, unresolved, and blackboxed threads.
* **Informational** shows pending, running, and informational threads.

Select **More filters** to filter by date range, event type, provider, origin, feature, or pair.

Use **Problems** and **Last 24 hours** first when investigating a new failure.

#### Sorting and pagination

Sort by newest or oldest first.

The list shows 25 threads or events per page. Problem items within large runs load and paginate separately.

### Understand statuses

#### Completed

The sync reached its finish event without recorded errors.

#### Resolved

The latest relevant event indicates a successful write or cleared unresolved state.

#### Failed

A write or sync operation reported an error.

A run is failed when its final summary contains errors.

#### Unresolved

CrossWatch could not complete the operation. It recorded the item for a later attempt.

#### Blackboxed

CrossWatch blocked the item after repeated failures. This prevents the same failure cycle.

#### Running or pending

**Running** has a start event but no finish event.

**Pending** has a planned or attempted operation without a final result.

### Review a thread

Select a thread to open its details.

The summary shows the outcome, provider route, item key, and current thread state. Titles and episode details appear when the recorder captured them.

#### Timeline

The **Timeline** tab lists thread events in chronological order.

Each entry can show its time, name, type, description, and reason code.

Run threads list their problem items. Select an item to open its full item thread.

#### Details

The **Details** tab groups information into:

* **Event** — status, item, reason, counts, and timestamps.
* **Route** — feature, operation, providers, origin, pair, and sync mode.
* **Current context** — current CrossWatch state, marked **live now**.

#### Raw data

The **Raw data** tab contains the archived payload and available identifiers.

These can include thread, run, event, pair, and item IDs. You can copy raw data, the item key, or the run ID.

{% hint style="warning" %}
Raw data can contain provider names, identifiers, and diagnostics. Remove sensitive information before sharing it publicly.
{% endhint %}

### Current context

For item threads, Events compares the historical record with current local state.

It can show:

* **Pair state** — whether the matching pair still exists and is active.
* **Provider health** — the latest available health state.
* **Unresolved, blackbox, and tombstone state** — whether current local records remain.
* **Analyzer findings** — current baseline findings for the item.

Select **View in Analyzer** when you need a current provider comparison.

A pair can show as unmatched after it is removed, recreated, or changed.

### Related threads

Threads with the same item key appear under **Related threads**.

Use them to identify earlier attempts, later successes, repeated unresolved results, or blackbox promotion.

Select a related thread to open it directly.

### Refresh and clear

Select **Refresh** to reload the archive, providers, and configured pairs.

Refresh keeps the active search, filters, page, and view where possible. Use it after a sync completes.

Select **Clear** to delete every archived event, thread, run summary, and import-tracking record.

{% hint style="danger" %}
Clear cannot be undone. It does not remove provider data, pair configuration, unresolved state, blackbox state, or tombstones.
{% endhint %}

New syncs record into a new archive after clearing.

### Recommended workflow

{% stepper %}
{% step %}

### Run the affected pair

Reproduce the issue with a normal sync.
{% endstep %}

{% step %}

### Open Problems

Open Events, select **Grouped**, then select **Problems**.

Limit the date range to **Last 24 hours** when needed.
{% endstep %}

{% step %}

### Review the item

Open the latest run, then select the affected item.

Read the Timeline and recorded reason.
{% endstep %}

{% step %}

### Compare current state

Review **Current context**.

Open Analyzer if you need a provider baseline comparison.
{% endstep %}

{% step %}

### Correct and verify

Correct the configuration, matching, or provider problem.

Run the pair again and confirm a later completed or resolved event.
{% endstep %}

{% step %}

### Acknowledge the thread

Acknowledge the reviewed record when investigation is complete.
{% endstep %}
{% endstepper %}

### Events and other tools

#### Events and Analyzer

Events explains what happened during a sync. Analyzer compares what exists now.

Use Events for operation history, routes, outcomes, and recorded reasons. Use Analyzer for current provider baselines, missing items, and identifier gaps.

#### Events and View details

Use View details while observing a running or recent operation.

Use Events for persistent history, repeated attempts, and item investigation. Application logs may still be needed for exception traces not represented by structured events.

### Local storage and maintenance

Events stores its SQLite archive at:

`<config>/.cw_databases/events.sqlite3`

A standard container installation usually uses:

`/config/.cw_databases/events.sqlite3`

Set `CROSSWATCH_EVENTS_DB` to override the archive location.

The archive remains local unless you store or back up the configuration directory elsewhere.

Use [Maintenance](/crosswatch/settings/maintenance) to check, optimize, or rebuild the archive.

* **Health check** verifies availability, integrity, schema, consistency, counts, timestamps, and storage size.
* **Optimize** checkpoints the write-ahead log and runs SQLite maintenance.
* **Rebuild** recreates the archive and can import supported local state records.

Rebuild does not restore every historical sync sequence. Use it only for a damaged archive or a health-check problem that Optimize cannot correct.

### Troubleshooting

#### A completed run appears in Open

The run is not acknowledged. Open means unacknowledged, not currently failing.

#### An unresolved event no longer matches current state

The event is historical. Check **Current context** to see whether the unresolved record still exists.

#### No events are visible

Run a sync, select **Refresh**, then select **All**.

Clear filters, widen the date range, and check archive health in Maintenance.

#### Provider health records are missing

Provider-health-only threads are hidden by default to reduce noise.

Select **All** or filter by provider or event type.

#### What to send with a support request

Copy the thread summary or raw data. Include the status, feature, route, item key, reason, run ID, and relevant timeline.

Remove credentials and other private information first.

### Limitations

* Events only shows structured information CrossWatch recorded.
* It does not replace application logs.
* Activity before Events existed can be incomplete.
* Rebuilding cannot guarantee every historical run.
* Current context can differ from the historical event.
* Acknowledgement never resolves, retries, or modifies an item.

### Related topics

* [Analyzer](/crosswatch/main-dashboard/tools/analyzer)
* [Maintenance](/crosswatch/settings/maintenance)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Scheduling](/crosswatch/settings/scheduling)


# Import / Export

Import media data into CrossWatch or export it for other services.

## Import / Export

The Import / Export tool lets you move your media data into or out of CW.

Use **Import** to bring existing watch history, ratings and watchlists from other trackers into the CW Tracker. Use **Export** to convert data available in CW into formats that can be used by other services.

### Import

The Import tab allows you to import existing tracker exports directly into CW.

Supported sources include:

1. Trakt
2. Letterboxd
3. Simkl
4. IMDb
5. TV Time
6. Yamtrack
7. Generic CSV or JSON files

CrossWatch can also automatically detect supported export formats.

Before importing, make sure the **CW Tracker** is configured under Connections. Imported data is written to the selected CW Tracker profile.

#### Importing data

1. Open **Tools**, then **Import / Export**.
2. Select the **Import** tab.
3. Select the source, or leave it on **Auto detect**.
4. Select the target CrossWatch Tracker profile.
5. Choose the export file.
6. Select which data you want to include.
7. Click **Preview file**.

Nothing is imported during the preview.

CW analyses the file and shows the detected items before anything is written to your tracker.

You can import:

**History**, watched movies, shows, seasons and episodes

**Ratings**, ratings contained in the source export

**Watchlist**, items saved to your watchlist

The preview can be filtered by media type, feature, title, year, ID and import status.

#### Preview status

Each item receives a status before import.

**Ready**, the item can be imported

**In tracker**, the item already exists in the target profile

**Duplicate**, the same item occurs more than once in the uploaded file

**Missing IDs**, CW could not find enough information to identify the item

**Invalid**, required information such as a watched date or rating is missing

**Unsupported**, the item or feature cannot currently be imported

By default, CrossWatch imports items marked as Ready.

Enable **Include existing** if you also want to process items that are already present in the CW Tracker. This can be useful when restoring or merging data.

You can import all ready items or manually select individual rows from the preview.

When you are satisfied with the preview, click **Import**.

### Export

The Export tab lets you export data available in CW to formats understood by other media services.

Select the source provider and, when applicable, the provider instance. You can then choose which data you want to export.

Available features include:

**Watchlist**

**History**

**Ratings**

**History & Ratings**

You can further filter the export by media type or search for specific titles, years or IDs.

Click **Preview** to inspect the matching items before creating the export.

By default, **All filtered** exports every item matching the current filters. Disable it if you only want to export selected rows.

Select the required export format and click **Export** to create the file.

Some formats only support specific features or media types. CW automatically adjusts the available options based on the selected export format.


# Mobile devices

Use CW on phones, switch compact/full UI, and install it as a PWA over HTTPS.

CW works well on phones. The UI exposes a lightweight mobile view by default.

### Mobile dashboard

Use the mobile dashboard when you just need status and quick checks.

It focuses on:

* **Statistics** and **Recent syncs**
* **Recent Activity** for recent local scrobbles
* The footer **Scrobble “Now Playing”** card (when enabled)
* A quick toggle between **Compact** and **Full UI**

### Install as an app (PWA)

You can install CW on Android and iOS as a “home screen app”.

Requirements:

* Serve the UI over **HTTPS**
* Reverse proxy with a trusted (public) certificate

Related: [HTTPS/TLS](/crosswatch/advanced-and-api/https-and-tls).

{% hint style="info" %}
Self-signed HTTPS is fine for encryption. You may still see browser warnings.
{% endhint %}

{% tabs %}
{% tab title="iPhone / iPad (Safari)" %}

1. Open CW in **Safari**
2. Tap **Share**
3. Tap **Add to Home Screen**
   {% endtab %}

{% tab title="Android (Chrome)" %}

1. Open CW in **Chrome**
2. Tap the **⋮** menu
3. Tap **Install app** (or **Add to Home screen**)
   {% endtab %}
   {% endtabs %}

### Summary

CW has a phone-friendly UI by default.

Use the mobile dashboard for quick status checks.

Install it as a PWA once the UI is served over HTTPS.

### Next steps

* Set up HTTPS so PWA install works: [HTTPS/TLS](/crosswatch/advanced-and-api/https-and-tls).
* Review local scrobble history: [Recent Activity](/crosswatch/settings/scrobbler/recent-activity).
* Enable or verify the “Now Playing” footer card if you use scrobbling: [Playing Card](/crosswatch/settings/scrobbler/playing-card).
* Review UI preferences and provider status in: [Settings](/crosswatch/settings).


# Settings

Configure providers, pairs, scheduling, UI options, and maintenance.

{% hint style="success" %}
CW has lots of settings but most users can leave the defaults alone.

Focus on the few things that matter:

* Connect providers in **Connections**
* Configure **TMDb** Metadata in **Connections**
* Create a sync pair in **Synchronization or/and Scrobbler**
  {% endhint %}

### Jump to a settings area

<table data-view="cards"><thead><tr><th>Area</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td><strong>Connections</strong><br>Connect Plex, Jellyfin, Emby, Trakt, and other providers.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/lCTegLtxemF834DhMv5R">/spaces/3rh5THg1PdhVsBt3GALo/pages/lCTegLtxemF834DhMv5R</a></td></tr><tr><td><strong>Metadata / ID Mapping</strong><br>Improve matching before you sync.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/8L38pVPE2gL0y3sD7I8D">/spaces/3rh5THg1PdhVsBt3GALo/pages/8L38pVPE2gL0y3sD7I8D</a></td></tr><tr><td><strong>Configure Pairs</strong><br>Choose direction, writes, and features.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/C1lNlN7T3a0xWpl0jEU5">/spaces/3rh5THg1PdhVsBt3GALo/pages/C1lNlN7T3a0xWpl0jEU5</a></td></tr><tr><td><strong>Scheduling</strong><br>Run pairs on a schedule without overlaps.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/hVUNomZiBduGHBlvojRV">/spaces/3rh5THg1PdhVsBt3GALo/pages/hVUNomZiBduGHBlvojRV</a></td></tr><tr><td><strong>Scrobbler</strong><br>Set up Watcher, Webhooks, and routes.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/YwgRnxeZmi2IG39XFA7l">/spaces/3rh5THg1PdhVsBt3GALo/pages/YwgRnxeZmi2IG39XFA7l</a></td></tr><tr><td><strong>UI settings</strong><br>Control theme, login, HTTPS, and dashboard widgets.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/vH3t2HJ1dWPYhjONtnAF">/spaces/3rh5THg1PdhVsBt3GALo/pages/vH3t2HJ1dWPYhjONtnAF</a></td></tr><tr><td><strong>Maintenance</strong><br>Backups, cache clears, logs, and recovery tools.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/IlKhfb6pyOgsPrOKgCe6">/spaces/3rh5THg1PdhVsBt3GALo/pages/IlKhfb6pyOgsPrOKgCe6</a></td></tr></tbody></table>

### Settings overview

The **Overview** screen is a dashboard. Use it to spot missing config fast.

This overview focuses on setup completeness.

The **Main** page focuses on live operational state.

{% hint style="info" %}
If setup feels overwhelming, finish connections first, then synchronization, then scheduling.
{% endhint %}

#### Quick actions

Use **Quick actions** to jump straight into common setup:

* **Open connections** (providers and metadata)
* **Open synchronization** (create source/target pairs)
* **Open scrobbler** (routes, webhook, filters)
* **Open UI settings** (UI and security)

#### Status overview

Use **Status overview** to confirm what CW currently detects:

* Connected providers
* Configured **Synchronization providers** and pair count
* **Metadata / ID Mapping** status
* **Scheduling** status
* **Scrobbler** status

Use [Main dashboard](/crosswatch/main-dashboard) and [Status dashboard](/crosswatch/main-dashboard/status-dashboard) when you want the live **Main** page view.

### Connections

**Connections** is the home for:

* Connected providers (tokens, URLs, profile IDs)
* **Metadata / ID Mapping** (TMDb and Anime ID Mapping)

#### Connect providers

Connect providers like Plex, Jellyfin, Emby, Trakt, SIMKL, and MDBList.

Providers appear in **Synchronization** only after they connect.

Related: [Connections](/crosswatch/settings/connections).

#### Metadata / ID Mapping

TMDb metadata improves matching across providers.

Anime ID Mapping improves AniList matching against TMDb, TVDb, and IMDb based providers.

Related:

* [Metadata / ID Mapping](/crosswatch/settings/connections/metadata)
* [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata)
* [Anime ID Mapping](/crosswatch/settings/connections/metadata/anime-id-mapping)

### Synchronization

Synchronization defines what syncs with what.

{% hint style="info" %}
The default pair settings are good for most users. Start with **one-way** and **one feature**
{% endhint %}

* **Direction**: source → target (example: Plex → Trakt).
* **Mode**: one-way or two-way.
* **Features**: Watchlist, History, Ratings, Progress (where supported).
* **Writes**: allow **Add** and/or **Remove**.

Related:

* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Synchronization](/crosswatch/settings/synchronization)

### Scheduling

Configure periodic runs.

Avoid overlaps. Start daily and tune later.

Related: [Scheduling](/crosswatch/settings/scheduling).

### Scrobbler

Real-time scrobbling uses **Watcher** (preferred) or **Webhooks**.

It requires connected providers (example: Plex + Trakt).

{% hint style="info" %}
Defaults are good here too. Add your scrobble **routes**, then enable **Watcher**.
{% endhint %}

If both paths are enabled, CW shows their status separately.

Related:

* [Scrobbler](/crosswatch/settings/scrobbler)
* [Watcher](/crosswatch/settings/scrobbler/watcher)

### UI settings

UI settings covers things like:

* dashboard visibility
* Recent Activity widget visibility and display mode
* web sign-in and Plex SSO

Related: [UI settings](/crosswatch/settings/ui-settings).

#### Security (UI login)

Protect CW with a username and password. Set both credentials, then click **Log-in to activate**.

Sessions expire when the browser closes by default. You can keep sessions for `1–365` days.

Review active browser sessions here. Use **Log out other sessions** to remove old sessions.

**Plex SSO**

You can optionally add **Sign in with Plex** for the web app.

This is an extra sign-in method.

Local CW credentials stay as the fallback and recovery path.

Guide: [Plex SSO](/crosswatch/settings/ui-settings/security/plex-sso).

**Trusted reverse proxies**

If you run CW behind a reverse proxy, set **Trusted reverse proxies** here.

This makes forwarded client IPs and scheme accurate.

It improves secure cookies and rate limiting.

Related: [Reverse proxies](/crosswatch/advanced-and-api/reverse-proxy-setup).

### Maintenance

Operational tools:

* Manage local state, caches, tracker data, and captures.
* Run targeted recovery and cleanup actions.
* Enable debug logging.
* Open backup and restore tools.

Related:

* [Maintenance](/crosswatch/settings/maintenance)
* [Backup & Restore](/crosswatch/settings/maintenance/backup-and-restore)

{% hint style="warning" %}
Maintenance actions affect local CW data.
{% endhint %}

### Saving and applying changes

* Use **Save** to persist changes.
* If a run is active, changes apply on the next run.


# Connections

Connect services  media servers, trackers, helpers and metadata

Providers are the services CW talks to.

Connect providers in **Settings → Connections**.

Then combine them in pairs.

{% hint style="success" %}
Best first setup: one media server and one tracker.
{% endhint %}

### Browse provider docs

<table data-view="cards"><thead><tr><th>Section</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td>Media servers</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/FVyiR13vuSOqiGlMiDbw">/spaces/3rh5THg1PdhVsBt3GALo/pages/FVyiR13vuSOqiGlMiDbw</a></td></tr><tr><td>Trackers</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/b27pu6Rt26LAGi446KRn">/spaces/3rh5THg1PdhVsBt3GALo/pages/b27pu6Rt26LAGi446KRn</a></td></tr><tr><td>Media clients</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/n5NxozCV3Z4zcY8zSMNX">/spaces/3rh5THg1PdhVsBt3GALo/pages/n5NxozCV3Z4zcY8zSMNX</a></td></tr><tr><td>Helpers and imports</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/1KYetEKG2fDCn2YMhBtE">/spaces/3rh5THg1PdhVsBt3GALo/pages/1KYetEKG2fDCn2YMhBtE</a></td></tr><tr><td>Metadata and mapping</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/8L38pVPE2gL0y3sD7I8D">/spaces/3rh5THg1PdhVsBt3GALo/pages/8L38pVPE2gL0y3sD7I8D</a></td></tr></tbody></table>

### Connect a provider

1. Choose a media server, tracker, media client, or helper.
2. Select **Connect** or **Sign In**.
3. Approve access and save the provider settings.

### Tips

* Add [Metadata / ID Mapping](/crosswatch/settings/connections/metadata) (TMDb)
* Start with **one-way** in your first pair.

### Next steps

* Build your first pair: [Configure Pairs](/crosswatch/settings/configure-pairs)
* Follow the guided setup order: [First-time setup](/getting-started/first-time-setup)


# Media servers

Supported media servers (Plex, Jellyfin, Emby) and setup notes for authentication, whitelisting, and matching.

Media servers are where your library and plays live. CrossWatch (CW) typically treats your media server as the source of truth.

{% hint style="success" %}
Start here if your library lives in Plex, Jellyfin, or Emby.
{% endhint %}

{% tabs %}
{% tab title="End users" %}
{% hint style="info" %}
Connect a media server in **Settings → Connections** before creating pairs.
{% endhint %}

### Supported media servers

<table data-view="cards"><thead><tr><th>Server</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td><strong>Plex</strong><br>Best external ID coverage and broad sync support.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/kYDK8rWWJHZJmYMDPvQo">/spaces/3rh5THg1PdhVsBt3GALo/pages/kYDK8rWWJHZJmYMDPvQo</a></td></tr><tr><td><strong>Jellyfin</strong><br>Self-hosted server with strong local control.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/Szfcj6x7Q0FfotF9xzXQ">/spaces/3rh5THg1PdhVsBt3GALo/pages/Szfcj6x7Q0FfotF9xzXQ</a></td></tr><tr><td><strong>Emby</strong><br>Media server with similar pair and scrobble support.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/GPW1AqoB7S8iPo29dgeW">/spaces/3rh5THg1PdhVsBt3GALo/pages/GPW1AqoB7S8iPo29dgeW</a></td></tr></tbody></table>

### Optional: whitelisting (recommended)

Whitelisting limits CW to specific libraries. It reduces noise, speeds up runs, and avoids syncing junk.

Guide: [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting).

#### Use whitelisting when

* you only want TV or anime
* the server has shared junk libraries
* you want faster dry runs

#### Skip it when

* the whole server is clean
* you want every library included
* you are still testing provider auth
  {% endtab %}

{% tab title="Power users" %}

### Library scoping matters

Whitelisting changes what CW thinks “exists”.

That affects both matching and deletion safety.

See: [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting).

### Matching note

Plex is usually strongest on external IDs.

Strict ID matching is now the default for Plex, Jellyfin, and Emby.

Emby/Jellyfin often need better metadata agents to reduce missing peers.

{% hint style="warning" %}
If matching quality is weak, fix metadata before enabling destructive writes.
{% endhint %}
{% endtab %}
{% endtabs %}


# Emby

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/L7cwnJ0EMgeznoVlyxVL" alt=""><figcaption></figcaption></figure>

### Connect Emby

1. Fill in your **Server URL**, **Username**, and **Password**.
2. Click **Sign In**. You should see the “Connected” banner.
3. Open **Settings** for Emby.
4. Verify these values are filled:
   * **Server URL**
   * **Username**
   * **User ID** (use **Auto-Fetch** if needed)
5. Click **Save**.

### Optional: whitelisting (libraries)

If you don’t see your libraries:

1. Click **Load Libraries**.
2. Pick the libraries you want CW to use.
3. Click **Save**.

Guide: [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting).

### Troubleshooting

* Make sure the **Server URL** is reachable from the CW container.
* Prefer a LAN IP over a hostname when debugging (example: `http://192.168.1.10:8096`).
* If sign-in works but libraries don’t load, check whitelisting and click **Load Libraries** again.
  {% endtab %}

{% tab title="Power users" %}

### Config keys

Stored under:

* `emby.*`

Common keys:

* `emby.server` / `emby.server_url`
* `emby.user_id`
* `emby.access_token` / `emby.api_key`

### Related docs

* Sync module: [Emby module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/JjJS0X5wKa8UCoQki63P)
* Adapter: [Adapter: Emby](/crosswatch/settings/synchronization/media-servers/emby)
  {% endtab %}
  {% endtabs %}


# Jellyfin

{% hint style="warning" %}
CrossWatch requires **Jellyfin 10.9 or newer**. Older Jellyfin versions are not supported.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/S7xYn03bqWzWm9nUhY1z" alt=""><figcaption></figcaption></figure>

### Connect Jellyfin

Jellyfin supports **Quick Connect** and username/password sign-in.

#### Quick Connect (recommended)

1. Enter your **Server URL**.
2. Click **Start Quick Connect**.
3. In Jellyfin, open your user menu and select **Quick Connect**.
4. Enter the displayed code and authorize the connection.
5. Open **Settings** for Jellyfin.
6. Verify these values are filled:
   * **Server URL**
   * **Username**
   * **User ID** (use **Auto-Fetch** if needed)
7. Click **Save**.

#### Username and password

Enter your **Server URL**, **Username**, and **Password**. Then click **Sign In**.

### Optional: whitelisting (libraries)

If you don’t see your libraries:

1. Click **Load Libraries**.
2. Pick the libraries you want CrossWatch to use.
3. Click **Save**.

Guide: [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting).
{% endtab %}

{% tab title="Power users" %}

### Config keys

Stored under:

* `jellyfin.*`

Common keys:

* `jellyfin.server` / `jellyfin.server_url`
* `jellyfin.user_id`
* `jellyfin.access_token`

### Related docs

* Sync module: [Jellyfin module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/8894e0a8c6c0655a44b11dde4a70cdb1b5c0c04c)
* Adapter: [Adapter: Jellyfin](/crosswatch/settings/synchronization/media-servers/jellyfin)
  {% endtab %}
  {% endtabs %}


# Plex

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/h8wxRy6cS9zQVLnV4euR" alt=""><figcaption></figcaption></figure>

### Connect Plex

1. Click **Connect Plex**.
2. A new tab opens and asks for a PIN.
3. Copy the PIN from **Link code** in CrossWatch. Paste it into the Plex page.
4. After Plex accepts the PIN, close the Plex tab.
5. Back in CrossWatch, open **Settings** for Plex.
6. Verify these values are filled:
   * **Server ID**
   * **Username**
   * **Account ID**
7. Click **Save**.

### Plex users: owner, managed users, and friends

Plex can return several user types in the user list:

* **Owner** — the Plex account that authenticated CrossWatch
* **Managed user** — a user inside the owner’s Plex Home
* **Friend or shared user** — a separate Plex account with access to one or more shared libraries

A friend or shared account is **not** the same as a Plex Home managed user.

#### Friend and shared account support

CrossWatch stores the token from the Plex account that completed the sign-in flow.

When you select a friend or shared user, CrossWatch can try to use Plex’s server-scoped shared token for that user.

That token is limited to your Plex Media Server.

It does **not** give CrossWatch full access to the friend’s Plex account, profile, watchlist, ratings, or other cloud data.

Current support:

* ✅ **History** — supported
* ✅ **Progress** — supported
* ❌ **Ratings** — not supported
* ❌ **Watchlist** — not supported

{% hint style="warning" %}
This depends on Plex’s shared-server token behavior.

Plex has changed this path before and may change or remove it again.
{% endhint %}

When requested user scope cannot be applied, CW stops the operation.

#### Plex Home managed users

Plex Home managed users work differently from friends.

CW can try to switch the authenticated Plex Home account into the selected managed user.

If the managed user is protected by a Plex Home PIN, configure the correct PIN too.

Use the server owner’s Plex profile for Plex Home managed users.

Use a separately authenticated Plex profile for friends when you need personal account features like watchlist or ratings.

### Optional: whitelisting (libraries)

If you don’t see your libraries:

1. Click **Load Libraries**.
2. Pick the libraries you want CW to use.
3. Click **Save**.

Guide: [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting).
{% endtab %}

{% tab title="Power users" %}

### Config keys

Stored under:

* `plex.*`

Common keys:

* `plex.account_token`
* `plex.server_id` / `plex.account_id` (when present)

### Related docs

* Sync module: [PLEX module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/35fa1d8db98b0b38d8f267852d3687b2a8ac4dd2)
* Adapter: [Adapter: Plex](/crosswatch/settings/synchronization/media-servers/plex)
  {% endtab %}
  {% endtabs %}


# Media clients

Connect supported media clients to synchronize library, history, and playback progress.

Media clients add library, history, ratings, progress, and watchlist data to CW pairs.

{% hint style="danger" %}

### Media-client sync warning

Media clients are best used as sync destinations.

Nuvio, Kodi, and Stremio are playback and library clients. They are not perfect history authorities. When used as a source, some data can be incomplete or approximate. Some data uses when CW first observed a change.

This matters most for history dates:

* Kodi uses `lastplayed` for watched history. Ratings and resume progress lack event timestamps. CrossWatch tracks when it first sees or changes those values.
* Nuvio provides watched and progress timestamps when available. Items without required dates are skipped or marked unresolved.
* Stremio stores exact watched dates for movies, but not individual episodes. Episode watched state syncs, but exact episode dates cannot be recovered.

For the safest setup, sync one-way from a provider to the media client. Only use media-client-to-provider or two-way sync if incomplete or approximate dates can sync back to providers.

Start with **Dry run**. Enable one feature at a time. Keep removals disabled until the plan looks correct.
{% endhint %}

### Available media clients

<table data-view="cards"><thead><tr><th>Client</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td><strong>Kodi</strong><br>Sync existing library items, watched state, ratings, and progress.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/EXPqYai39u3gbg8AL0sd">/spaces/3rh5THg1PdhVsBt3GALo/pages/EXPqYai39u3gbg8AL0sd</a></td></tr><tr><td><strong>Nuvio</strong><br>Sync selected-profile history, progress, and library entries.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/0o7ACTLUstQN2Fvkwxxy">/spaces/3rh5THg1PdhVsBt3GALo/pages/0o7ACTLUstQN2Fvkwxxy</a></td></tr><tr><td><strong>Stremio</strong><br>Sync History, Progress, Watchlist, and supported Ratings.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/vSqNM9MDlwnSSeHRS3Hy">/spaces/3rh5THg1PdhVsBt3GALo/pages/vSqNM9MDlwnSSeHRS3Hy</a></td></tr></tbody></table>

### Supported features

| Client  | History        | Ratings               | Progress       | Watchlist      |
| ------- | -------------- | --------------------- | -------------- | -------------- |
| Kodi    | Read and write | Read and write        | Read and write | Not supported  |
| Nuvio   | Read and write | Not supported         | Read and write | Read and write |
| Stremio | Read and write | Write to Stremio only | Read and write | Read and write |

History and progress support movies and individual episodes. Watchlists support movies and shows. Seasons are not synchronized separately.

{% hint style="danger" %}
Media clients can store less detail than trackers or Media servers. Start with one-way sync, then run **Dry run** before writing data.
{% endhint %}

### What you can lose

<table><thead><tr><th width="107.333251953125">Client</th><th>Key limitation</th></tr></thead><tbody><tr><td><strong>Kodi</strong></td><td>Separate plays collapse into one play count and latest date. Decimal ratings are rounded. Missing library items cannot be added.</td></tr><tr><td><strong>Nuvio</strong></td><td>Repeated plays become one history record. Progress needs a position, duration, and timestamp. Episodes and seasons cannot enter the watchlist.</td></tr><tr><td><strong>Stremio</strong></td><td>Episode watched dates are lost. Numeric ratings become reactions. Only one unfinished episode per series retains progress.</td></tr></tbody></table>

{% hint style="info" %}
Keep a tracker as the source of truth when exact dates, ratings, or several active episodes matter.
{% endhint %}

### Data handling reference

Support does not guarantee full fidelity. Some clients retain the latest state only. Others retain dated events. Review these limits before configuring a pair.

#### What can be lost or changed

| Client  | Feature         | When used as source                                                        | When used as destination                                                       | What you may lose                                                                     |
| ------- | --------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| Kodi    | History         | CW reads the watched state, play count and `lastplayed` value              | CW writes the play count and last played date to an existing Kodi library item | Separate play events are not preserved, only the current play count and latest date   |
| Kodi    | History         | A watched item can exist without a usable `lastplayed` value               | When the source has no usable date, Kodi receives the current date and time    | The original watched date                                                             |
| Kodi    | History         | Only items in the selected Kodi libraries are available                    | The movie or episode must already exist in Kodi                                | Items that cannot be matched remain unresolved                                        |
| Kodi    | Ratings         | Kodi provides a rating from 1 to 10                                        | Kodi accepts an integer rating from 1 to 10                                    | Decimal ratings are rounded                                                           |
| Kodi    | Ratings         | Kodi does not provide the original rating date                             | CW records when the rating was first observed or changed                       | The actual date on which the user rated the item                                      |
| Kodi    | Progress        | Kodi provides the resume position and duration                             | CW writes the resume position to an existing library item                      | The original progress timestamp is not available                                      |
| Kodi    | Progress        | CW records when progress was first observed or changed                     | Completion behaviour depends on the Kodi completion setting                    | A high resume position may be treated as completed by Kodi                            |
| Kodi    | Watchlist       | Not available                                                              | Not available                                                                  | Watchlists cannot be synchronized through Kodi                                        |
| Nuvio   | History         | Nuvio provides a watched date for movies and episodes                      | Nuvio accepts the watched date provided by the source                          | Separate repeated plays are not preserved as separate events                          |
| Nuvio   | History         | Data is read from the selected Nuvio profile                               | Data is written to the selected Nuvio profile                                  | Data belonging to other Nuvio profiles is not included                                |
| Nuvio   | History         | Movies and episodes require usable identifiers                             | CW must resolve the matching Nuvio content and episode                         | Items with missing or incorrect mapping remain unresolved                             |
| Nuvio   | Ratings         | Not available                                                              | Not available                                                                  | Ratings cannot be synchronized through Nuvio                                          |
| Nuvio   | Progress        | Nuvio provides position, duration and a progress timestamp                 | All three values are required when writing progress                            | Progress without a duration or timestamp cannot be written                            |
| Nuvio   | Progress        | Progress is linked to a specific movie or episode                          | Progress at 90 percent or higher falls under the Nuvio completion policy       | High progress can be treated as completed                                             |
| Nuvio   | Watchlist       | The Nuvio Library is used as the watchlist                                 | Movies and shows are added to the Nuvio Library                                | Nuvio does not have a separate CW watchlist model                                     |
| Nuvio   | Watchlist       | Movies and shows are supported                                             | Episodes and seasons are not supported                                         | Episode and season list entries are ignored or unresolved                             |
| Stremio | Movie history   | Stremio provides the latest movie watched date and watched state           | Stremio stores one latest movie watched date                                   | Separate historical play events are not preserved                                     |
| Stremio | Movie history   | Stremio has a watched count, but CW synchronizes the current watched state | CW marks the movie watched and stores one latest date                          | The exact number and dates of previous plays                                          |
| Stremio | Episode history | Stremio stores watched episodes in a bitfield                              | Stremio stores only whether each episode is watched                            | Every individual episode watched date                                                 |
| Stremio | Episode history | CW can identify which episodes are watched, but not when they were watched | A source episode date cannot be stored in the Stremio bitfield                 | Episode dates cannot survive a round trip through Stremio                             |
| Stremio | Ratings         | Stremio cannot be used as a ratings source                                 | Ratings from another provider are converted to Stremio reactions               | The original numeric rating and rating date                                           |
| Stremio | Ratings         | No ratings are read from Stremio                                           | Ratings from 6 are written as `liked`, ratings from 8 are written as `loved`   | Ratings below 6 do not create a reaction                                              |
| Stremio | Ratings         | Episode ratings are not available                                          | Only movies and shows can receive reactions                                    | Episode ratings                                                                       |
| Stremio | Progress        | A movie can expose its current resume position                             | Stremio requires a position and duration                                       | Progress without a duration cannot be written                                         |
| Stremio | Progress        | A series record exposes one active episode and resume position             | Writing progress for another episode changes the active episode                | Multiple unfinished episodes from the same series cannot be retained at the same time |
| Stremio | Watchlist       | The Stremio Library is treated as the watchlist                            | Movies and shows are added to the Stremio Library                              | There is no separate want to watch list                                               |
| Stremio | Watchlist       | All listed movies and shows can appear as watchlist items                  | Episodes and seasons are not supported                                         | Watched library items may also be included when Stremio is the source                 |
| Stremio | All features    | Reliable matching normally requires an IMDb identifier                     | CW may use TMDb metadata to resolve an IMDb identifier                         | Items without a usable mapping remain unresolved                                      |

#### Important history differences

**Exact history versus watched state**

Trackers can store dated history events.

Media clients usually store a current state, such as watched or not watched, together with no date or only one latest date.

When a client does not provide the original date, CW cannot recreate it.

The destination will either use its own import date, use another fallback or leave the item unresolved, depending on the destination provider.

**Multiple watches**

Kodi, Nuvio and Stremio are synchronized as one current history record per movie or episode.

A tracker may contain several separate plays for the same item.

When tracker history is synchronized to a media client, those separate plays can collapse into one watched state.

A later sync back to the tracker cannot restore the original individual events.

#### Common pair examples

| Pair                        | What is synchronized                                        | Main risk                                                                                            |
| --------------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Stremio to Plex             | Movie and episode watched state                             | Stremio episodes have no individual watched date, Plex marks them watched using the import date      |
| Plex to Stremio             | Movie history, episode watched state and supported progress | Individual Plex episode dates are discarded after they are written to Stremio                        |
| Stremio to Trakt            | Movie dates and episode watched state                       | The original episode dates are unavailable                                                           |
| Trakt to Stremio            | Movie and episode watched state                             | Separate Trakt plays collapse into one state, episode dates are not retained                         |
| Provider to Stremio Ratings | Movie and show ratings                                      | Numeric ratings become `liked` or `loved`, ratings below the configured threshold create no reaction |
| Kodi to a tracker           | Watched state, latest watched date, ratings and progress    | Missing Kodi dates and synthetic rating or progress timestamps are not historical source data        |
| Tracker to Kodi             | History, ratings and progress for matching library items    | Missing watched dates become the current date, decimal ratings are rounded                           |
| Nuvio to a tracker          | Watched dates and progress from the selected profile        | Repeated plays are represented as one current item and other profiles are excluded                   |
| Tracker to Nuvio            | History, progress and watchlist                             | High progress may be treated as completed                                                            |
| Any provider to Kodi        | Supported data for movies and episodes already in Kodi      | CW cannot add missing media to the Kodi library                                                      |
| Any provider to Nuvio       | Supported data for resolvable movies and episodes           | Incorrect content or episode mapping prevents the write                                              |
| Any provider to Stremio     | Supported data using Stremio compatible identifiers         | Episode history dates, exact ratings and multiple episode progress entries can be lost               |

#### Two-way sync risks

Two-way sync can send reduced data back to the provider that originally contained more detail.

For example, a tracker can send exact episode dates to Stremio. Stremio stores only the watched state. During a later sync from Stremio to the tracker, those original dates are no longer available.

The same applies to exact ratings converted into Stremio reactions and multiple episode progress entries reduced to one active Stremio episode.

Two-way sync is not recommended when a client stores less information.

{% hint style="warning" %}
Do not use Stremio as a ratings source. Keep exact episode dates and ratings authoritative elsewhere.
{% endhint %}

For adapter-level behavior, see [Media clients synchronization](/crosswatch/settings/synchronization/media-clients).


# Kodi

Connect Kodi through HTTP JSON-RPC for library sync and playback scrobbling.

{% hint style="warning" %}
**Media-client sync warning:** Review [Media clients](/crosswatch/settings/connections/media-clients) before using a media client as a source or enabling two-way sync.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/Inc5yC9tlUmeyPZMowJb" alt=""><figcaption></figcaption></figure>

Connect CW to a Kodi media client through HTTP JSON-RPC.

Kodi manages media in local or network libraries. CrossWatch connects directly to Kodi’s HTTP JSON-RPC interface.

{% hint style="warning" %}
Kodi support is experimental. It requires Kodi 21 Omega or newer and JSON-RPC 13.5.0 or newer.
{% endhint %}

{% hint style="info" %}
CW is tested with a default Kodi 21+ installation. No additional add-ons are required.

* Kodi supports extensive customization, third-party add-ons, skins, and modified configurations. CrossWatch cannot support every possible Kodi setup. Disable or isolate customizations when troubleshooting a connection or sync issue.
* Kodi provides limited detailed information. CW applies additional intelligence where possible, but this remains limited by the data Kodi provides.
* **Disable other Kodi scrobble/tracking add-ons before syncing with CW**
  {% endhint %}

### What it supports

* **History:** movies and episodes.
* **Ratings:** movies and episodes.
* **Progress:** movies and episodes.
* **Watcher scrobbling:** movies and episodes.

Kodi ratings use the 1–10 user rating scale.

Watchlists and playlists are not supported. Series and seasons do not synchronize separately.

### Prepare Kodi

1. Open Kodi.
2. Open **Settings** → **Services** → **Control**.
3. Enable **Allow remote control via HTTP**.
4. Note the configured port.
5. Keep **Require authentication** enabled. Configure a username and password.
6. Confirm the CW host can reach the Kodi address and port.

{% hint style="danger" %}
Kodi disables its web server by default. Do not expose the Kodi web server to the internet.
{% endhint %}

**Allow remote control from applications on other systems** is not required. That setting controls WebSocket, TCP, and EventServer access. CW uses authenticated HTTP.

### Connect Kodi

1. Open **Settings** → **Connections** → **Media clients** → **Kodi**.
2. Enter the Kodi server URL, such as `http://192.168.1.50:8080`.
3. Enter the Kodi username and password when authentication is enabled.
4. Enable **Verify SSL** only for HTTPS with certificate validation.
5. Click **Connect Kodi**.
6. Confirm the detected Kodi version.
7. Click **Save**.

CW verifies JSON-RPC, confirms the server is Kodi, and checks both required versions.

### Whitelisting

Open the **Whitelisting** tab after connecting Kodi. Click **Load libraries** to retrieve Kodi video source paths.

Configure separate source paths for History, Ratings, Progress, and Scrobbling. An empty selection includes every Kodi video source.

Whitelisting uses the full Kodi source path. Files below a selected path are included automatically.

### Multiple Kodi clients

Use Profiles to connect more than one Kodi client.

Each profile has its own server URL, credentials, SSL setting, and feature whitelists. Pairs and Watcher routes can target a specific profile.

**Kodi's built-in user profiles are not supported.** They are separate from CrossWatch Profiles.

Start with the default profile. Add profiles after the first connection works correctly.

### Troubleshooting

#### Kodi server is unreachable

Confirm the server URL and port. The address must be reachable from the CW host or container.

Do not use `localhost` unless Kodi shares the same container or network namespace. Check firewall rules for the Kodi HTTP port.

#### Kodi rejected the credentials

Confirm the configured username and password under **Settings** → **Services** → **Control** in Kodi.

#### Server is not Kodi

The address responded, but CW could not identify Kodi. Confirm that the URL points directly to Kodi’s HTTP server.

#### Kodi or JSON-RPC version is too old

CW requires Kodi 21 Omega and JSON-RPC 13.5.0 or newer.

#### Libraries are not shown

Add video sources in Kodi, then click **Load libraries** again. An empty whitelist includes every source. CW does not support any thid-party plugins.

#### Items remain unresolved

The movie or episode must already be in Kodi’s library. Add accurate IMDb, TMDb, or TVDb identifiers.
{% endtab %}

{% tab title="Power users" %}

### Provider capabilities

The Kodi module supports bidirectional history, ratings, and progress synchronization. It also supplies playback data to Watcher.

#### History

**Supported content:** movies and episodes.

**Supported operations:** read, add, and remove watched state.

Writes apply only to matched movies and episodes already indexed by Kodi.

#### Ratings

**Supported content:** movies and episodes.

**Supported operations:** read, add or update, and remove.

Kodi user ratings use an integer scale from 1 to 10.

#### Progress

**Supported content:** movies and episodes.

**Supported operations:** read, add or update, and remove.

Standalone series and season progress records are unsupported.

#### Watcher scrobbling

Kodi is a Watcher source for movies and episodes. CrossWatch polls active playback through HTTP JSON-RPC.

Live TV playback is ignored. Routes can use separate Kodi profiles and scrobble whitelists.

### Matching and library requirements

CrossWatch primarily matches Kodi movies and episodes with IMDb, TMDb, and TVDb identifiers.

Writes require an existing, uniquely matched Kodi library item. CrossWatch does not create media files or library entries.

Items without a safe match remain unresolved. Correct external identifiers in Kodi before retrying.

### Whitelisting behavior

Kodi retrieves video source paths from its library. Configure independent path allowlists for History, Ratings, Progress, and Scrobbling.

A blank allowlist includes every source. A selected source includes all files below its full Kodi path.

### Profiles and instances

The default Kodi profile stores configuration under `kodi.*`.

Additional profiles are stored under `kodi.instances.<profile>.*`. Each profile stores its server URL, credentials, SSL setting, and separate feature allowlists.

Pairs and Watcher routes select a specific Kodi profile. Start with the default profile before creating additional connections.

### Config keys

Common keys are stored under `kodi.*`:

* `kodi.server`
* `kodi.username`
* `kodi.password`
* `kodi.verify_ssl`
* `kodi.history.libraries`
* `kodi.ratings.libraries`
* `kodi.progress.libraries`
* `kodi.scrobble.libraries`

Additional profiles use `kodi.instances.<profile>.*`.

### Connection validation

CrossWatch uses JSON-RPC to validate the configured endpoint. It confirms the server identity and checks Kodi and JSON-RPC versions.

Kodi can require HTTP Basic Auth for each request. Keep authentication enabled whenever possible.

### Current limitations

* The adapter is experimental.
* Kodi 21 Omega and JSON-RPC 13.5.0 are minimum requirements.
* Watchlists and playlists are unsupported.
* Series and seasons are not standalone sync items.
* Live TV playback is not scrobbled.
* CrossWatch never creates Kodi library entries.
* Kodi's built-in user profiles are unsupported.

### Related docs

* [Profiles](/crosswatch/settings/connections/profiles)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Watcher](/crosswatch/settings/scrobbler/watcher)
  {% endtab %}
  {% endtabs %}


# Nuvio

{% hint style="warning" %}
**Media-client sync warning:** Review [Media clients](/crosswatch/settings/connections/media-clients) before using a media client as a source or enabling two-way sync.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/ZLdKJyNXedC4UFtpyNrn" alt=""><figcaption></figcaption></figure>

Connect Nuvio through TV login, select a profile, and synchronize your library, history, and resume progress.

Nuvio is an open-source media center and library companion. It is not a media server or tracker. CrossWatch connects to Nuvio’s account service and uses the selected profile’s data.

{% hint style="warning" %}
Nuvio support is experimental. API changes can temporarily affect this integration.\
\
**TMDb Metadata is basicaly required for Nuvio.** Without it, synchronization are severely limited.
{% endhint %}

### What it supports

* **Watchlist:** movies and shows.
* **History:** movies and episodes.
* **Progress:** movies and episodes.

CW Watchlist maps to the Nuvio Library.

Ratings and playlists are not supported. Nuvio is not a Watcher source or scrobble target.

### Connect Nuvio

1. Open **Settings** → **Connections** → **Clients** → **Nuvio**.
2. Click **Connect Nuvio**.
3. Approve the displayed temporary code on Nuvio’s approval page.
4. Return to CrossWatch after approval is detected.
5. Select the Nuvio profile to synchronize.
6. Click **Save**.

CW selects the profile automatically when only one profile exists.

No API key is required. CrossWatch stores authentication tokens and your selected profile. It never stores your Nuvio password.

### Nuvio profiles

Nuvio profiles differ from CW provider profiles.

A CrossWatch provider profile is a complete Nuvio connection. A Nuvio profile selects the viewer data within that connection.

Each CW provider profile can use a different Nuvio profile. If the selected profile disappears, CrossWatch marks the connection unavailable until you select another.

### Recommended first setup

1. Configure [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata).
2. Connect Nuvio and select the correct profile.
3. Create a one-way pair with Nuvio as source or target.
4. Enable one feature and keep **Dry run** enabled.
5. Test Watchlist before History and Progress.

### Continue Watching

Connected Nuvio profiles appear in **Continue Watching**.

You can view progress, change resume percentage, remove progress, and mark items watched.

Progress updates require a known duration. CrossWatch cannot reliably update items without one.

### Troubleshooting

#### The approval page does not open

Allow popups for CrossWatch. The connection window also shows a manual approval link and code.

#### The login code expired

Click **Connect Nuvio** again to generate a new code.

#### Nuvio is authenticated but not connected

Select a Nuvio profile and click **Save**. Authentication alone is insufficient.

#### Selected profile unavailable

The profile was removed or Nuvio cannot return it. Reconnect Nuvio or choose another profile.

#### Token refresh failed

Disconnect Nuvio, then complete the connection again.

#### Items are unresolved

Confirm that the source has a TMDb, IMDb, or TVDb ID. Configure TMDb metadata when no direct TMDb ID exists.

#### Nuvio service unavailable or invalid response

Retry later and check for a newer CrossWatch version. Nuvio API changes can require an adapter update.
{% endtab %}

{% tab title="Power users" %}

### Provider capabilities

The Nuvio module is bidirectional. It verifies changes after writing them.

#### Watchlist

**Supported content:** movies and shows.

**Supported operations:** read, add, and remove.

Nuvio watchlist synchronization uses the Nuvio Library. CrossWatch reads the library, applies changes, then writes the resulting library. Existing entries and available metadata remain intact.

CrossWatch reads the library again after writing. It confirms only expected additions and removals.

Avoid manual Library changes during an active Watchlist run.

#### History

**Supported content:** movies and episodes.

**Supported operations:** read, add, and remove watched items.

History writes need a valid watched timestamp. Episode writes include the series ID, season number, and episode number.

Shows and seasons are not written as watched entities.

#### Progress

**Supported content:** movies and episodes.

**Supported operations:** read, add or update, and remove.

Progress writes require a content ID, playback position, duration, and progress timestamp.

Shows and seasons have no standalone progress records. The adapter uses standard season and episode numbering.

### Matching and IDs

Nuvio content IDs use TMDb or IMDb values. TMDb is the preferred write identity:

`tmdb:550`

Existing Nuvio records can use IMDb IDs:

`tt0137523`

When the source has only IMDb or TVDb IDs, CrossWatch uses TMDb metadata to resolve a TMDb content ID.

TMDb can enrich new Nuvio Library entries with titles, release years, descriptions, genres, posters, and backgrounds.

### Profiles and instances

The default provider profile stores configuration under `nuvio.*`.

Additional provider profiles use independent connections in the provider instances structure. Each profile has separate tokens and a selected Nuvio profile.

Pairs select a provider profile using `source_instance` or `target_instance`.

### Config keys

Stored under `nuvio.*`:

* `nuvio.base_url` — API base URL. Defaults to `https://api.nuvio.tv`.
* `nuvio.access_token` — temporary API access token.
* `nuvio.refresh_token` — token used to refresh access.
* `nuvio.expires_at` — token expiry as an epoch timestamp.
* `nuvio.profile_id` — selected numeric Nuvio profile ID.
* `nuvio.profile_name` — selected Nuvio profile name.

There is no configurable public client key. CrossWatch supplies the required Nuvio client details.

Do not copy tokens between Nuvio profiles or CrossWatch provider profiles.

### Token refresh

CrossWatch refreshes access tokens when they are missing or nearly expired.

After an authentication error, CrossWatch attempts one refresh and retries the request. Reconnect when the refresh token is invalid.

### Connection health check

The health check confirms authentication, the selected profile, and read access to progress, history, and the Nuvio Library.

Possible states include:

* `missing_authentication`
* `profile_unavailable`
* `token_refresh_failed`
* `auth_failed`
* `invalid_response`
* `service_unavailable`

### Current limitations

* The adapter is experimental.
* Ratings, playlists, and collections are unsupported.
* Watchlist supports movies and shows only.
* History and progress support movies and episodes only.
* Progress writes require a known duration.
* Nuvio is not a Watcher source or scrobble target.
* TMDb Metadata is required. Without it, matching and synchronization are severely limited.
* Nuvio API changes can require a CrossWatch update.

### Related docs

* [Profiles](/crosswatch/settings/connections/profiles)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata)
  {% endtab %}
  {% endtabs %}


# Stremio

Connect Stremio to synchronize History, Progress, Watchlist, and supported Ratings.

{% hint style="warning" %}
**Media-client sync warning:** Review [Media clients](/crosswatch/settings/connections/media-clients) before using a media client as a source or enabling two-way sync.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/RFdBAvqBClNgmel7LJHh" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Stremio support is experimental. CW uses Stremio's internal account API. It can change without notice.
{% endhint %}

### What it supports

* **History:** read and write for movies and episodes.
* **Progress:** read and write for movies and episodes.
* **Watchlist:** read and write for movies and shows.

CW uses the Stremio Library as the Watchlist.

**Ratings** support writes from another provider to Stremio. Stremio cannot be a Ratings source.

**Playlists** are not supported.

### Connect Stremio

1. Open **Settings** → **Connections** → **Media clients** → **Stremio**.
2. Enter your Stremio email address and password.
3. Select **Connect Stremio**.
4. Wait until the connection shows **Stremio connected**.

CW uses your credentials once to request an auth key. CW does not store your email address or password. It stores only the returned auth key.

### Connection profiles

Each Stremio connection represents one Stremio account.

Stremio account profiles are not supported. Create another CW connection profile for another account.

### History

#### Movies

Stremio stores watched state, latest watched date, and watched count.

CW synchronizes watched state and the latest available watched date.

Stremio does not expose every play event and date. CW cannot recover a complete viewing history for repeatedly watched movies.

#### Episodes

Stremio stores watched state for each episode. It does not store an individual watched date.

When Stremio is the source, other providers can show episodes watched on the sync date. The original dates cannot be recovered.

When CW writes dated History to Stremio, it retains watched state but discards episode dates.

{% hint style="warning" %}
Do not use Stremio as the source of truth when historical episode dates matter.
{% endhint %}

### Progress

Stremio stores playback position and duration for movies and episodes.

Writing Progress requires a duration. CW can resolve a missing duration through configured metadata when possible.

Stremio keeps one active episode Progress entry per series. Writing Progress for another episode can replace the active entry.

Writing Progress does not automatically mark an item as watched.

### Watchlist

CW treats the Stremio Library as the Watchlist.

Movies and shows are supported. Episodes and seasons are not Watchlist items.

Listed Library items can return as Watchlist items, including already watched titles.

### Ratings

Stremio supports Ratings as a destination only. CW converts ratings from another provider into reactions.

By default:

1. Ratings below 6 create no reaction.
2. Ratings from 6 through 8 become **Liked**.
3. Ratings from 8 through 10 become **Loved**.

Only movies and shows are supported. Episode Ratings cannot be written.

Stremio stores reactions, not numeric ratings. A rating of 8 and 10 both become **Loved**. CW cannot read reactions as their original numeric rating.

### Matching

Stremio relies mainly on IMDb identifiers.

CW can use configured TMDb metadata to resolve an IMDb identifier, poster, or runtime.

Items can remain unresolved when:

1. No IMDb identifier is available.
2. Stremio or Cinemeta cannot resolve the show.
3. The season or episode cannot be matched.
4. Progress has no usable position or duration.
5. The selected feature does not support the item type.

### Recommended setup

1. Use a provider with complete dated History as the source of truth.
2. Use Stremio as a History and Ratings destination.
3. Start with a one-way pair and run **Dry run**.
4. Review unresolved items before enabling **Remove** or two-way sync.
5. Create a capture before importing a large History.

### Common pair examples

#### Plex to Stremio

Movie History, episode watched state, and supported Progress synchronize to Stremio.

Stremio discards individual episode watched dates. CW writes only items mapped to Stremio identifiers.

#### Stremio to Plex

Movies can include their latest available watched date.

Episodes have watched state but no individual watched date. Plex can show imported episodes as watched on the sync date.

#### Trakt to Stremio

Movie and episode watched state synchronize to Stremio.

Separate Trakt play events become the current Stremio watched state. Individual episode dates are not retained.

#### Stremio to Trakt

Movie History can include the latest available movie date.

Stremio cannot preserve episode watched dates.

#### Provider Ratings to Stremio

Movie and show Ratings become **Liked** or **Loved** reactions.

The original numeric rating cannot be recovered from Stremio.

### Disconnect Stremio

Disconnecting removes the stored Stremio auth key from CW.

It does not change existing History, Progress, Library items, or reactions in Stremio.

CW may require you to remove or update pairs that still use the connection.

### Troubleshooting

#### Stremio rejected the credentials

Verify the email address and password by signing in to Stremio. Then reconnect.

#### Stremio API is unreachable

Check CW's internet connection and test again. The internal Stremio API may be temporarily unavailable.

#### Missing IMDb identifier

Configure [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata) and retry. Review unresolved items when no IMDb identifier exists.

#### Stremio episode unresolved

Confirm that the show exists in Cinemeta. Check that source season and episode numbering match Stremio.

#### Stremio duration missing

CW cannot calculate Progress from a percentage without a duration. Configure TMDb metadata or use a source with a runtime.

#### Episode watched dates changed after synchronization

Stremio does not store individual episode watched dates. CW cannot recover them later.

#### Unexpected Watchlist items

CW uses every listed Stremio Library item as the Watchlist. Watched items remain included.

#### Exact rating disappeared

Stremio stores reactions rather than numeric ratings. The source provider remains authoritative.
{% endtab %}

{% tab title="Power users" %}

### Authentication

CW authenticates through Stremio's internal login endpoint.

The email address and password are sent to Stremio during connection. Stremio returns an `authKey`, which CW stores as `auth_key`.

CW clears credentials after the connection attempt. It does not write them to the configuration.

CW treats the auth key as a secret. It encrypts the key when saving the configuration.

The internal API is not part of Stremio's public addon API. An API change can break the connection until CW is updated.

### Configuration

The default Stremio configuration is:

```json
{
  "stremio": {
    "auth_key": "",
    "ratings": {
      "liked_min": 6.0,
      "loved_min": 8.0
    }
  }
}
```

Use the connection interface. Do not store a Stremio email address or password in the configuration.

### Rating thresholds

Configure reaction thresholds under `stremio.ratings`:

```json
{
  "stremio": {
    "ratings": {
      "liked_min": 5.0,
      "loved_min": 9.0
    }
  }
}
```

`liked_min` sets the lowest rating that becomes **Liked**. `loved_min` sets the lowest rating that becomes **Loved**.

`loved_min` cannot be lower than `liked_min`. CW adjusts it when necessary.

Ratings below `liked_min` are skipped. Stremio Ratings remain destination-only.

### Stremio data model

CW reads and updates Stremio `libraryItem` records.

#### Movie History

Movie History uses:

```
state.lastWatched
state.timesWatched
state.flaggedWatched
```

A movie is watched when `timesWatched` or `flaggedWatched` exceeds zero.

CW uses `lastWatched` as the watched date. It can use the record modification time as a fallback. Complete play history is unavailable.

#### Episode History

Episode History uses:

```
state.watched
```

This serialized bitfield records episode watched states. It does not include per-episode timestamps.

CW does not expose record modification time as an episode watched date. The timestamp can reflect an unrelated series change.

CW maps episodes through Stremio and Cinemeta video lists. It uses the show IMDb identifier, season, and episode number.

#### Progress

Progress uses:

```
state.timeOffset
state.duration
state.video_id
state.season
state.episode
```

Movie records contain the active movie position. Series records contain one active episode, position, and duration.

A later write for another episode replaces those active fields. CW reads, merges, and writes the record to preserve unrelated fields.

#### Watchlist

CW treats the Stremio Library as the Watchlist. An item is listed when:

```
removed = false
temp = false
```

CW includes watched listed items. Removing a Watchlist item changes Library membership but preserves History.

#### Ratings

CW writes Ratings through Stremio's reactions service:

```
liked
loved
```

CW stores pair-scoped state for reactions it writes. This supports bookkeeping and duplicate prevention. It does not make Stremio a Ratings source.

### Identifiers and metadata

IMDb is the primary Stremio identifier.

Episodes require a show IMDb identifier, season number, and episode number.

TMDb metadata can resolve a missing IMDb identifier, poster, or duration. It does not guarantee a match. Episode ordering, specials, and provider numbering can still prevent matching.

### Supported item types

* **History:** movies and episodes.
* **Progress:** movies and episodes.
* **Watchlist:** movies and shows.
* **Ratings:** movies and shows, destination only.

Shows, seasons, and episodes are not interchangeable. Feature support varies by item type.

### Operational behavior

CW reads the current Stremio record before writing. It merges only fields required by the chosen feature.

For example, writing Progress preserves the episode watched bitfield. Writing History preserves unrelated Library metadata.

History and Watchlist removals are supported. Stremio uses present-state semantics.

When a relevant state disappears, CW can treat it as a removal if removal synchronization is enabled.

Enable **Remove** only after a successful initial sync. Confirm that the Stremio Library is your intended Watchlist.

### Related docs

* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata)
* [Profiles](/crosswatch/settings/connections/profiles)
  {% endtab %}
  {% endtabs %}


# Trackers

Connect tracker providers, including CW Local Tracker and Floppy, for synchronization and scrobbling.

Trackers store your watchlists, ratings, and watch history. CW syncs between trackers and media servers using pairs.

{% tabs %}
{% tab title="End users" %}
{% hint style="info" %}
Connect a tracker in **Settings → Connections** before creating pairs.
{% endhint %}

{% hint style="success" %}
Pick one tracker first. Expand later if you need more destinations.
{% endhint %}

### Available trackers

<table data-view="cards"><thead><tr><th>Tracker</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td><strong>Trakt</strong><br>Popular choice for history, watchlist, and scrobbling.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/GTsgKYIFvITuCFrzQvji">/spaces/3rh5THg1PdhVsBt3GALo/pages/GTsgKYIFvITuCFrzQvji</a></td></tr><tr><td><strong>SIMKL</strong><br>Strong movie and TV tracking with scrobble support.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/TiyMexyBNNHgfQ4JDyy6">/spaces/3rh5THg1PdhVsBt3GALo/pages/TiyMexyBNNHgfQ4JDyy6</a></td></tr><tr><td><strong>MDBList</strong><br>Useful for curated list workflows and tracker syncing.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/I8ilfYpZ7YJR54a1BgZw">/spaces/3rh5THg1PdhVsBt3GALo/pages/I8ilfYpZ7YJR54a1BgZw</a></td></tr><tr><td><strong>PublicMetaDB</strong><br>Extra source for shared public metadata lists.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/vjyLYE8gHX2sWPG9DZSB">/spaces/3rh5THg1PdhVsBt3GALo/pages/vjyLYE8gHX2sWPG9DZSB</a></td></tr><tr><td><strong>AniList</strong><br>Anime-first tracker with separate matching needs.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/0kr4lay0HZW2P0SduuT1">/spaces/3rh5THg1PdhVsBt3GALo/pages/0kr4lay0HZW2P0SduuT1</a></td></tr><tr><td><strong>TMDb</strong><br>Useful for metadata and some tracker-style workflows.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/ZgT0Gg3fSsAcn0rT9hed">/spaces/3rh5THg1PdhVsBt3GALo/pages/ZgT0Gg3fSsAcn0rT9hed</a></td></tr><tr><td><strong>CW Local Tracker</strong><br>Local watchlist, ratings, history, and progress storage with snapshots.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/rzxIwN0wCvk2DzF9ebgx">/spaces/3rh5THg1PdhVsBt3GALo/pages/rzxIwN0wCvk2DzF9ebgx</a></td></tr><tr><td><strong>Floppy</strong><br>Experimental watchlist, ratings, and history synchronization through a self-hosted server.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/267qV8g5yO1tK8LKIUpf">/spaces/3rh5THg1PdhVsBt3GALo/pages/267qV8g5yO1tK8LKIUpf</a></td></tr></tbody></table>

### Tips

* Start with **one tracker** as your “source of truth”.
* Add [Metadata](/crosswatch/settings/connections/metadata) for better matching.
* Use [CW Local Tracker](/crosswatch/settings/connections/trackers/crosswatch) for local snapshots before risky changes.
  {% endtab %}

{% tab title="Power users" %}

### Choosing a tracker

* Trakt / SIMKL / MDBList / PublicMetaDB are best for movies + TV.
* AniList is anime-first and won’t match everything cleanly cross-provider.

### Rate limits

Trackers are more likely to rate limit than local media servers.

If you see 429s:

* sync less often
* reduce batch size
* add write delays (pair/provider settings)

{% hint style="warning" %}
If you write to several trackers at once, start with slower schedules.
{% endhint %}
{% endtab %}
{% endtabs %}


# AniList

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/UoR9qYEU8XbkW8hteiD1" alt=""><figcaption></figcaption></figure>

Connect AniList to synchronize supported anime activity with CrossWatch.

### Connect AniList

1. Create an AniList developer app:
   1. Open <https://anilist.co/settings/developer>
   2. Click **Create New Client**
   3. Set **Name**: `CrossWatch`
   4. Set **Redirect URL**: the callback shown by CrossWatch, for example: `http://<your-server-ip>:8787/callback/anilist`
2. Copy the **Client ID** and **Client Secret** into CrossWatch.
3. Click **Connect AniList**.
4. Approve access. Wait until you see:

```
AniList authorized. You can close this tab and return to the app.
```

{% hint style="warning" %}
If you later switch CrossWatch from **HTTP ↔ HTTPS** (or change host/port), update the AniList **Redirect URL** to match.

Guide: [HTTPS/TLS](/crosswatch/advanced-and-api/https-and-tls).
{% endhint %}

### Troubleshooting

* If the auth tab doesn’t open, disable popup blockers for CrossWatch.
* If authorization fails, double-check the redirect URL matches exactly.
* AniList tokens expire after \~1 year. Reconnect yearly.
  {% endtab %}

{% tab title="Power users" %}

## Power users

### Config keys

Stored under:

* `anilist.*`

### Related docs

* Sync module: [AniList module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/JYaSOwNqdnIZBN5Q71sz)
* Adapter: [Adapter: AniList](/crosswatch/settings/synchronization/trackers/anilist)
  {% endtab %}
  {% endtabs %}


# CrossWatch

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/I2xiIW2sdkViILPtENZ0" alt=""><figcaption></figcaption></figure>

Use the local CrossWatch tracker for private, on-device synchronization storage.

### Connect Local Tracker

CW Local Tracker runs inside CW. It needs no external account, API key, PIN, or OAuth application.

CW Tracker is now a dedicated connection. It no longer appears in **Settings** → **UI settings**.

1. Open CW.
2. Go to **Settings** → **Connections** → **Trackers** → **CrossWatch**
3. Click **Connect local tracker**.

CW enables the local tracker as a normal connection. Its status changes to **Connected**.

Create additional profiles when another user or sync setup needs separate storage.

### Configure the local tracker

Open the **Settings** tab in the CW Local Tracker connection.

#### Label

Enter a short internal label for the tracker profile. Labels support up to 12 characters.

#### Retention days

Set how long CW retains snapshots. The default is 30 days.

Set this value to `0` to retain snapshots indefinitely.

#### Auto snapshot

When enabled, CW creates a snapshot before writing local tracker changes. This setting is enabled by default.

#### Max snapshots per feature

Set the maximum snapshots retained for each feature: watchlist, history, ratings, and progress.

The default is 64 snapshots per feature. Set this value to `0` for unlimited snapshots.

#### Restore snapshots

Select the snapshot used for each feature. **Latest** is the default and uses the newest available snapshot.

You can select a specific snapshot for watchlist, history, ratings, or progress.

### Troubleshooting

* If the connection fails, confirm the CW configuration directory exists and is writable.
* If recent changes are missing, confirm **Restore snapshots** is set to **Latest**.
* If snapshots disappear early, check **Retention days** and **Max snapshots per feature**.
* If the main connection cannot be removed, delete additional tracker profiles first.

Remove additional profiles with their profile delete button.
{% endtab %}

{% tab title="Power users" %}

## Power users

### Storage

Local tracker data is stored in the CW configuration directory:

```
/config/.cw_provider
```

Additional profiles are stored below:

```
/config/.cw_provider/profiles/<profile>
```

The `/config` directory must be writable and persistent. Deleting its volume can delete local tracker data and snapshots.

### Limitations

CW Local Tracker supports bidirectional synchronization for watchlist, ratings, history, and playback progress.

Playlists are not supported.

The adapter has no fixed item limit. Capacity depends on available storage, synchronized data, and snapshot retention.

The local tracker is a local source and destination. It is not a remote tracking service or cloud backup.
{% endtab %}
{% endtabs %}


# Floppy

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/MzKsI2RrW6AQeyuSN8xV" alt=""><figcaption></figcaption></figure>

Connect a Floppy server to synchronize supported activity with CrossWatch.

{% hint style="warning" %}
Floppy is experimental. Floppy API changes can affect the connection or supported features.
{% endhint %}

### Connect Floppy

Floppy connects to CW with its server URL and an API token.

#### Get an API token

1. Open Floppy.
2. Go to **Settings** → **Integrations**.
3. Create or copy an API token.

#### Connect Floppy

1. Open CW.
2. Go to **Settings** → **Connections** → **Authentication** → **Floppy**.
3. Enter the Floppy server URL. For example:

   ```
   http://<floppy-server>:8000
   ```
4. Enter the API token from Floppy.
5. Enable **Verify SSL** when Floppy uses HTTPS with a trusted certificate.
6. Click **Connect Floppy**.

CW validates the server URL and API token through the Floppy API. Its status then changes to **Connected**.

Create additional profiles to connect multiple Floppy servers or users.

### What it supports

#### Watchlist

Movies and shows are supported. CW uses the configured Floppy list name.

The default list name is **Watchlist**. CW creates the list when needed.

#### Ratings

Movies and shows are supported. Ratings use a scale from 0 to 10.

#### History

Movies and individual episodes are supported. Show-level and season-level history are not supported.

#### Progress

Movies and individual episodes are supported. Show-level and season-level progress are not supported.

All supported Floppy synchronization requires TMDb identifiers.

### Troubleshooting

* If the API token is invalid, create a new token under **Floppy Settings** → **Integrations**.
* If CW cannot reach Floppy, confirm its URL is accessible from the CW host or container.
* When both apps use Docker, do not use `localhost` unless they share a container. Use the Floppy container name, host address, or another reachable address.
* If SSL validation fails, verify the certificate and **Verify SSL** setting.
* If CW reports an unexpected response, confirm Floppy is supported and exposes `/api/v1`.
* Items without a TMDb identifier cannot synchronize. CW reports them as unresolved.
* If Floppy returns `429 Too Many Requests`, reduce the Floppy provider throttling values.
  {% endtab %}

{% tab title="Power users" %}

## Power users

### Limitations

Floppy support is experimental.

Playlists are not supported.

CW does not define validated stable volume limits for Floppy.

Floppy reads are paginated in batches of up to 200 items. CW applies an internal safety limit of 1,000 pages.

This is a CW safety limit. It is not a confirmed Floppy capacity limit.

The default provider throttling allows 20 GET requests and 20 POST requests per second.
{% endtab %}
{% endtabs %}


# MDBList

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/r2T4ZtkleD0SKSnv1j8m" alt=""><figcaption></figcaption></figure>

Connect MDBList to synchronize supported lists and activity with CrossWatch.

{% hint style="info" %}
Use **Device Code** for new setups. It is the default and recommended auth mode.

**API key** auth still works as a fallback.
{% endhint %}

### Connect MDBList with Device Code

1. Open **Settings → Connections**.
2. Set **Authentication Method** to **Device Code**.
3. Click **Connect MDBList**.
4. A browser window opens so you can enter your code.
5. Copy the **Link code (PIN)** from CrossWatch.
6. Paste the code into MDBList.
7. Wait until CrossWatch shows **Connected with Device Code**.

### Use the legacy API key mode

1. Open <https://mdblist.com/preferences/#api>
2. Create or copy your MDBList API key.
3. In CrossWatch, set **Authentication Method** to **API key**.
4. Paste the API key into CrossWatch.
5. Click **Connect MDBList**.

### Troubleshooting

* If the auth window does not open, disable popup blockers for CrossWatch.
* If the link code fails, click **Connect MDBList** again to generate a new code.
* If you use API key mode and auth fails, regenerate the key and reconnect.
* If writes fail, verify items have stable IDs (TMDb/IMDb) in the source provider.
* If you see `429 Too Many Requests`, lower MDBList rate limits.\
  Guide: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries).
  {% endtab %}

{% tab title="Power users" %}

## Power users

### Auth modes

* `Device Code` — recommended
* `API key` — legacy fallback

### Config keys

Stored under:

* `mdblist.rate_limit.get_per_sec`, `mdblist.rate_limit.post_per_sec`
* `mdblist.api_key` when you use legacy API key auth

CrossWatch stores the MDBList token automatically when you use Device Code auth.

Rate-limit defaults and tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries)

### Related docs

* Sync module: [MDBList module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/f495c828c87856a3585f8011e21a444293045001)
* Adapter: [Adapter: MDBList](/crosswatch/settings/synchronization/trackers/mdblist)
  {% endtab %}
  {% endtabs %}


# PublicMetaDB

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/HEHPP5ZrPFKKwvmJv4Lg" alt=""><figcaption></figcaption></figure>

Connect PublicMetaDB to synchronize supported activity with CrossWatch.

{% hint style="warning" %}
**PublicMetaDB support is experimental.**

Prefer a one-way flow into PublicMetaDB.

Start with your media server as the source and PublicMetaDB as the target.

TMDb Metadata is required for proper enrichment.
{% endhint %}

{% hint style="info" %}
PublicMetaDB uses an API key.

CrossWatch can also enrich missing titles and release years for PublicMetaDB items in watchlist, history, ratings, and progress.

That enrichment needs [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata) and a TMDb API key.

It runs only when the PublicMetaDB item already has a TMDb ID and is missing the field.
{% endhint %}

### Connect PublicMetaDB

1. Open **Settings → Connections**.
2. Open **PublicMetaDB**.
3. In PublicMetaDB, create an API key in **Settings → API**.
4. Paste the key into **API Key**.
5. Click **Connect**.
6. If needed, click **Verify** once.

API keys usually start with `pm-`.

### Profiles

PublicMetaDB supports multiple profiles.

Use **Profile** to switch between `Default` and extra profiles.

Click **New** to add a profile.

Click **Delete** to remove a non-default profile.

Guide: [Profiles](/crosswatch/settings/connections/profiles)

### Troubleshooting

* “Connected” only means CrossWatch saved an API key.
* **Verify** checks the key against PublicMetaDB.
* Missing titles or years usually mean TMDb metadata is not configured, the item has no TMDb ID, or the field already came from PublicMetaDB.
* If verify fails, rotate the key in PublicMetaDB and reconnect.
  {% endtab %}

{% tab title="Power users" %}

## Power users

### Config keys

Stored under:

* `publicmetadb.api_key`
* `publicmetadb.instances.<profile>.api_key`

Common shared settings:

* `publicmetadb.base_url`
* `publicmetadb.watchlist_name`
* `publicmetadb.watchlist_list_id`
* `publicmetadb.ratings_label`
* `publicmetadb.rate_limit.get_per_sec`
* `publicmetadb.rate_limit.post_per_sec`

Pair-level watchlist overrides are stored under:

* `providers.publicmetadb.watchlist_name`

### Auth behavior

CrossWatch stores the API key and marks the provider as connected.

A deeper health check validates the key against PublicMetaDB.

CrossWatch sends the key as a bearer token.

### Related docs

* Profiles: [Profiles](/crosswatch/settings/connections/profiles)
* Pair setup: [Configure Pairs](/crosswatch/settings/configure-pairs)
* Watchlist settings: [Watchlist](/crosswatch/settings/configure-pairs/features/watchlist)
* Progress settings: [Progress](/crosswatch/settings/configure-pairs/features/progress)
  {% endtab %}
  {% endtabs %}


# PunchPlay

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/pohvsmxmIxXK4EmiseOx" alt=""><figcaption></figcaption></figure>

PunchPlay is a media tracker for movies and TV shows. CrossWatch synchronizes your activity with supported providers.

{% hint style="warning" %}
**PunchPlay support is experimental.** Functionality may change as the integration improves.
{% endhint %}

### What it supports

| Feature   | Support                              |
| --------- | ------------------------------------ |
| Watchlist | Movies and shows                     |
| Ratings   | Movies, shows, seasons, and episodes |
| History   | Movies and episodes                  |
| Progress  | Movies and episodes                  |
| Watcher   | Yes                                  |

PunchPlay can be a source or target in synchronization pairs.

It can also receive playback activity from **Watcher**.

### Connect PunchPlay

PunchPlay uses device-code login. You do not need an API application or API key.

1. Open **Settings** → **Authentication**.
2. Open **PunchPlay**.
3. Click **Connect PunchPlay**.
4. CrossWatch displays a code and opens PunchPlay’s linking page.
5. Enter the code and approve CrossWatch.
6. Return to CrossWatch.

PunchPlay shows **Connected** after approval.

CrossWatch stores the required access and refreshes tokens automatically.

### Using PunchPlay

After connecting, PunchPlay is available when creating synchronization pairs.

Examples include:

`Plex → PunchPlay`

`PunchPlay → Trakt`

`SIMKL → PunchPlay`

Enable features supported by both providers.

Select PunchPlay as a **Watcher** destination for real-time playback updates.

### Multiple profiles

PunchPlay supports CrossWatch Profiles.

Each profile uses separate authentication. Choose profiles independently in pairs and Watcher routes.

Keep the default profile when using one PunchPlay account.

### Troubleshooting

* Reconnect under **Settings** → **Authentication** → **PunchPlay** if the connection fails.
* Configure metadata when sync items cannot match. PunchPlay works best with provider IDs.
* Report reproducible integration issues through the CrossWatch GitHub issue tracker.
  {% endtab %}

{% tab title="Power users" %}

## Power users

PunchPlay is an experimental CrossWatch sync provider and Watcher destination.

Current integration version: `0.1`.

### Authentication

PunchPlay uses the OAuth device-code flow.

CrossWatch provides the PunchPlay application ID. Users do not configure client credentials.

The flow requests these scopes:

`profile:read`

`history:read`

`history:write`

`lists:read`

`lists:write`

`ratings:read`

`ratings:write`

`collection:read`

`collection:write`

`playback:read`

`playback:write`

`events:read`

CrossWatch stores and refreshes access tokens automatically.

Do not manually edit these tokens.

### Provider capabilities

#### Watchlist

Read and write are supported for:

`movies`

`shows`

Season and episode watchlist entries are unsupported.

#### Ratings

Read, write, and remove are supported for:

`movies`

`shows`

`seasons`

`episodes`

PunchPlay uses a **1–10** rating scale. CrossWatch rounds writes to whole numbers.

#### History

Read, write, and remove are supported for:

`movies`

`episodes`

History writes require a watched timestamp.

#### Progress

Read, write, and remove are supported for:

`movies`

`episodes`

PunchPlay stores playback state separately from watched history. CrossWatch syncs unfinished playback when supported.

### IDs

PunchPlay writes use available identifiers:

`TMDb`

`IMDb`

`TVDb`

`MAL`

PunchPlay returns TMDb as its primary CrossWatch identifier.

Progress accepts `TMDb`, `IMDb`, and `TVDb`.

Use good metadata coverage when pairing PunchPlay with another provider.

### Watcher

PunchPlay is available as a Watcher destination.

CrossWatch sends these playback events:

`start`

`pause`

`resume`

`progress`

`stop`

CrossWatch maintains a playback session ID for each session.

Movies send available TMDb, IMDb, or TVDb IDs.

Episodes also send season and episode numbers with available show IDs.

PunchPlay can mark an item watched when playback exceeds the configured threshold.

### Rate limiting

CrossWatch uses conservative PunchPlay rate limits. It reacts to API rate-limit responses.

Default configuration:

```json
"punchplay": {
  "timeout": 20.0,
  "max_retries": 3,
  "history_per_page": 100,
  "history_max_pages": 5000,
  "rate_limit": {
    "get_per_sec": 2.0,
    "post_per_sec": 0.5,
    "bulk_per_min": 30,
    "playback_per_5min": 120,
    "sync_read_per_min": 120
  }
}
```

Most installations should keep these values unchanged.

PunchPlay bulk operations use batches of up to **100 items**. CrossWatch batches requests automatically.

### Profiles

PunchPlay supports multiple CrossWatch provider profiles.

Each profile stores separate authentication data:

`access_token`

`refresh_token`

`expires_at`

`refresh_expires_at`

`username`

`user_id`

`device_id`

Pairs select a source or target PunchPlay profile. Watcher routes select a destination profile independently.

### Experimental status

The PunchPlay adapter is marked **experimental**.

API behavior and supported capabilities may change.

Use the UI for normal installations. Do not edit PunchPlay credentials in `config.json`.
{% endtab %}
{% endtabs %}


# Scrob

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/PfbVAe0vxo6ieU9AbRfz" alt=""><figcaption></figcaption></figure>

Connect your self hosted [Scrob](https://github.com/ellite/scrob) instance to CW.

Scrob can be used as both a source and a target in CW synchronization pairs.

{% hint style="warning" %}
**Scrob support is experimental.** Functionality may change as the integration improves.
{% endhint %}

### What it supports

| Feature   | Support                                  |
| --------- | ---------------------------------------- |
| Watchlist | Movies and shows                         |
| Ratings   | Movies, shows, seasons, and episodes     |
| History   | Movies and episodes, including rewatches |
| Progress  | Movies and episodes                      |
| Watcher   | Source and sink                          |

All supported features can be synchronized from or to Scrob.

### Connect Scrob

Open **Settings** → **Connections** → **Trackers** → **Scrob**.

Enter:

* **Server URL** — the URL of your Scrob instance, such as `http://192.168.1.100:7330`.
* **API Key** — your Scrob API key.
* **Username** — your Scrob username.
* **Password** — your Scrob password.

If your account uses two-factor authentication, enter the current six-digit code.

Select **Connect**.

When CW reports **Connected**, save your settings.

### Using Scrob

After connecting, select Scrob as a source or target when creating synchronization pairs.

Examples include:

`Trakt → Scrob`

`Scrob → Trakt`

`Plex → Scrob`

Enable the features supported by both providers.

For a first sync, use one-way sync, one feature at a time, and **Dry Run**.

Enable writes or two-way synchronization after reviewing the result.

Select Scrob as a **Watcher** source or destination for real-time playback.

Watcher routes support Scrob as either a source or a sink.

Webhook mappings support Scrob only as a sink.

### Playback progress

Progress is a normal CW sync feature. It works like Watchlist, Ratings, and History.

CW synchronizes existing playback positions with providers that support Progress.

Scrob keeps Continue Watching progress between `5%` and `90%`.

### Watcher

Scrob participates in CW Watcher routes.

When Scrob is a source, CW reads its active playback sessions.

When Scrob is a destination, CW sends playback activity to it.

Use **Sync** for stored Watchlist, Ratings, History, and Progress data.

Use **Watcher** for active playback and real-time scrobbling.
{% endtab %}

{% tab title="Power users" %}

## Power users

Scrob is implemented as a native CW provider.

The integration currently has experimental status.

### Architecture

Scrob can operate in three CW roles:

1. **Sync provider**
2. **Watcher source**
3. **Scrobble sink**

The sync adapter is bidirectional and can therefore be selected on either side of a CW sync pair.

In Watcher routes, Scrob can be a source or a sink.

In webhook mappings, Scrob can only be a sink.

### Sync Capabilities

#### Watchlist

Supported media:

* Movies
* Shows

CW maps the Scrob watchlist to a Scrob personal list named:

`Watchlist`

If this list does not exist when CW needs to write to it, CW creates it as a private list.

The list name can be overridden using:

`watchlist_name`

Supported operations:

* Read
* Add
* Remove

#### Ratings

Supported media:

* Movies
* Shows
* Seasons
* Episodes

Rating scale:

`1 to 10`

CW supports reading, creating, updating and removing ratings.

Episode ratings require the episode to already be resolvable by Scrob.

#### History

Supported media:

* Movies
* Episodes

Scrob stores individual play events rather than only a single watched state.

CW therefore preserves:

* Individual `watched_at` timestamps
* Multiple plays of the same title
* Rewatches
* History additions
* History removals

This allows Scrob to operate as both a history source and a history target without reducing multiple plays to a single watched flag.

#### Progress

Supported media:

* Movies
* Episodes

Progress is implemented as a native CW sync feature.

It participates in normal sync pairs alongside Watchlist, Ratings and History.

Supported operations:

* Read progress from Scrob
* Add or update progress in Scrob
* Remove progress from Scrob
* One way synchronization
* Two way synchronization

CW reads Scrob Continue Watching state and converts it into the normal CW progress model.

When writing progress to Scrob, CW requires:

* A supported media identifier
* Playback duration
* Playback percentage

Scrob keeps Continue Watching progress above `5%` and below `90%`.

Progress outside that range is not written as an active resume position.

This Progress sync functionality is separate from CW Watcher and Scrobbler functionality.

The **Progress** tab in a sync pair controls synchronization of stored playback progress between providers.

### IDs and Matching

The Scrob sync adapter primarily uses TMDB identifiers.

Movies and shows normally require a TMDB ID.

Episode operations also use show context, including:

* Show TMDB ID
* Season number
* Episode number

TVDB and IMDb identifiers can also be used by some playback paths when available.

For reliable synchronization, configure metadata resolution in CW so missing identifiers can be resolved before data reaches the Scrob adapter.

### Authentication

CW stores the Scrob connection information per provider instance.

Required configuration:

```
server_url
api_key
username
password
```

The Scrob API key is sent using:

```http
X-Api-Key
```

After login, CW also uses the Scrob access token:

```http
Authorization: Bearer <token>
```

CW refreshes the Scrob login when the stored access token expires.

### Two Factor Authentication

TOTP based two factor authentication is supported.

When Scrob requests two factor authentication, CW asks for the current 6 digit TOTP code and completes the login flow.

CW can also request a new code when reauthentication is required later.

### OIDC Only Installations

The current CW integration authenticates to Scrob using the Scrob username and password flow.

If password login has been disabled on the Scrob server and the installation only allows OIDC authentication, CW cannot currently establish the required Scrob session.

### API Detection

CW automatically checks the Scrob API using:

```
/
```

and:

```
/api/proxy
```

Normally this does not need to be configured manually.

The configured Server URL must be reachable from the CW container, not only from your browser.

For example, if both applications run on the same Docker network, an internal container hostname can be used.

Otherwise use a reachable LAN address or reverse proxy URL.

### SSL Verification

CW provides a **Verify SSL** option for Scrob.

Enable this when Scrob is exposed through HTTPS using a certificate trusted by the CW container.

Private installations using self signed certificates may fail verification unless the certificate chain is trusted by the CW container.

### Scrob as a Watcher Source

Scrob can be selected as a CW Watcher source.

CW reads active playback sessions from the Scrob Now Playing API.

These sessions are converted into normal CW playback events:

* Start
* Pause
* Resume
* Progress
* Stop

The resulting events can then be routed to compatible CW scrobble targets.

For example:

```
Scrob → Trakt
Scrob → SIMKL
Scrob → MDBList
```

The Scrob watcher uses polling to detect playback state and automatically backs off when the Scrob server becomes unavailable.

CW also identifies sessions created by CW itself to prevent Scrob routes from immediately returning through Scrob and creating a playback loop.

### Scrob as a Scrobble Sink

Scrob can also be selected as the destination of a Watcher route.

For example:

```
Plex → Scrob
Jellyfin → Scrob
Emby → Scrob
```

CW converts its internal playback events into the playback webhook format understood by Scrob.

This allows start, pause, resume, progress and stop activity to update Scrob without waiting for a normal sync run.

When the configured watched threshold is reached, the final stop event can also result in a completed play in Scrob history.

### Sync Versus Watcher

Sync and Watcher solve different problems.

Use **Sync** for:

* Watchlist
* Ratings
* Historical plays
* Playback Progress
* Existing Continue Watching state
* Backfills
* Periodic reconciliation

Use **Watcher** for:

* Active playback
* Real time playback state
* New completed plays
* Scrobbling between providers

A common setup is therefore:

```
Initial Sync
    ↓
Periodic Sync
    +
Watcher for active playback
```

### Scrob API Documentation

Scrob provides its own OpenAPI documentation.

On a standard Scrob installation:

```
https://<your-scrob-server>/docs
```

provides Swagger UI.

```
https://<your-scrob-server>/redoc
```

{% endtab %}
{% endtabs %}


# SIMKL

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/6RmTnfd9Qu7MNKkUwOhq" alt=""><figcaption></figcaption></figure>

Connect SIMKL to synchronize supported activity and configure real-time scrobbling.

{% hint style="info" %}
SIMKL is also used for scrobbling (Watcher/Webhooks) when configured.
{% endhint %}

### Connect SIMKL

#### Connect with PIN

PIN is the recommended method. It does not need a SIMKL developer app.

1. Open **CrossWatch**.
2. Go to **Settings** → **Connections** → **Authentication** → **SIMKL**.
3. Select **PIN**.
4. Click **Connect SIMKL**.
5. CrossWatch shows a short authorization code.
6. Enter the code at [SIMKL PIN](https://simkl.com/pin).
7. Approve the connection.
8. Return to CrossWatch.

CrossWatch checks the authorization status automatically. It then shows **Connected using PIN**.

{% hint style="info" %}
CrossWatch opens the SIMKL PIN page in a new browser tab. If blocked, open [SIMKL PIN](https://simkl.com/pin) manually.
{% endhint %}

If the code expires, click **Restart** and repeat the authorization.

#### Connect with OAuth

OAuth is optional. Use it with your own SIMKL developer app.

1. Open [SIMKL developer settings](https://simkl.com/settings/developer/).
2. Click **Add** → **Add a new App**.
3. Set **Name** and **Description** to `CrossWatch`.
4. Set **Redirect URI** to the callback URL shown by CrossWatch. For example: `http://<your-server-ip>:8787/callback`.
5. Click **Save Changes**.
6. Copy the **Client ID** and **Client Secret** into CrossWatch.
7. Select **OAuth**, then click **Connect SIMKL**.
8. Approve the connection in SIMKL.

{% hint style="warning" %}
If you change the host, port, or protocol, update the OAuth **Redirect URI**.

See [HTTPS/TLS](/crosswatch/advanced-and-api/https-and-tls).
{% endhint %}

### Limitations

* Watchlist: stable up to \~2,500 items total (max 10,000)
* History (plays): stable up to \~10,000 play events total.
* Ratings: stable up to \~10,000 ratings total.

Prefer incremental windows for big backfills.

### Troubleshooting

* If the PIN page does not open, allow popups for CrossWatch.
* If the PIN expires, click **Restart** to get a new code.
* If OAuth fails, verify that the redirect URI matches exactly.
* If you run sync very often, SIMKL may throttle you. Daily is usually enough.
* If you see `429 Too Many Requests`, tune provider throttling.\
  Guide: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries).
  {% endtab %}

{% tab title="Power users" %}

## Power users

### Config keys

Stored under:

* `simkl.*`

Common keys:

* `simkl.client_id`, `simkl.client_secret`
* `simkl.access_token`

### Related docs

* Sync module: [SIMKL module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/6d90d0a66a006bf6af4d6a75e6db99e974447856)
* Adapter: [Adapter: SIMKL](/crosswatch/settings/synchronization/trackers/simkl)
  {% endtab %}
  {% endtabs %}


# TMDb

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/IGiRLyZe08Z8h8EcNGXj" alt=""><figcaption></figcaption></figure>

Connect TMDb (Sync) to synchronize supported TMDb account activity.

{% hint style="info" %}
If you only want ID matching and cover arts then you want **Metadata → TMDb**, not TMDb (Sync).
{% endhint %}

### Connect TMDb (Sync)

{% stepper %}
{% step %}

### Create a TMDb API key (v3)

1. Create a TMDb account.
2. Request an API key: <https://www.themoviedb.org/settings/api>
   {% endstep %}

{% step %}

### Connect in CW

1. Open **Settings → TMDb (Sync)**.
2. Paste your **TMDb API Key (v3)**.
3. Click **Connect**.
4. A TMDb tab opens. Approve the request.
5. Return to CW. Wait for “Connected”.

If it stays pending, click **Verify** once.
{% endstep %}
{% endstepper %}

### Disconnect

1. Open **Settings → TMDb (Sync)**.
2. Click **Disconnect**.

### Troubleshooting

* Auth tab didn’t open: disable popup blockers for CW.
* Stuck on “Pending approval”: approve in the TMDb tab, then click **Verify**.
* “TMDb did not return a session id”: restart the flow with **Connect** and approve again.
* Verify keeps failing: the server must reach `https://api.themoviedb.org/3`.
* Key issues: the key must be a valid TMDb **v3 API key**.

### Security notes

Treat your TMDb **API key** and **session** as secrets.

They are stored in CW config on disk.
{% endtab %}

{% tab title="Power users" %}

## Power users

### Config keys used

TMDb (Sync) stores values under:

* `tmdb_sync.api_key`
* `tmdb_sync.session_id`

Metadata stores a separate key under:

* `tmdb.api_key`

### API endpoints (manual flow)

<details>

<summary>Start / Verify / Finish / Disconnect</summary>

CW exposes a small API used by the UI.

These are useful for debugging or headless setup.

#### Start (opens the approval URL)

`POST /api/tmdb_sync/connect/start`

```bash
curl -X POST http://localhost:PORT/api/tmdb_sync/connect/start \
  -H "Content-Type: application/json" \
  -d '{"api_key":"YOUR_KEY"}'
```

Open the returned `auth_url` and approve.

#### Verify (auto-finish after approval)

`GET /api/tmdb_sync/verify`

```bash
curl http://localhost:PORT/api/tmdb_sync/verify
```

#### Finish (optional explicit completion)

`POST /api/tmdb_sync/connect/finish`

```bash
curl -X POST http://localhost:PORT/api/tmdb_sync/connect/finish \
  -H "Content-Type: application/json" \
  -d '{"api_key":"YOUR_KEY","request_token":"REQUEST_TOKEN"}'
```

#### Disconnect

`POST /api/tmdb_sync/disconnect`

```bash
curl -X POST http://localhost:PORT/api/tmdb_sync/disconnect
```

</details>
{% endtab %}
{% endtabs %}


# Trakt

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/cCjhCZqRN8xKKjG1oqag" alt=""><figcaption></figcaption></figure>

Connect Trakt to synchronize supported activity and configure real-time scrobbling.

{% hint style="danger" %}

### VIP account required

Trakt requires a VIP account for apps. Free Trakt accounts cannot use this integration.
{% endhint %}

{% hint style="info" %}
Trakt is also used for scrobbling (Watcher/Webhooks) when configured.
{% endhint %}

### Connect Trakt

1. Create a Trakt OAuth app:
   1. Open <https://trakt.tv/oauth/applications>
   2. Click **New Application**
   3. Set **Name**: `CrossWatch`
   4. Set **Description**: `CrossWatch`
   5. Set **Redirect URI**: `urn:ietf:wg:oauth:2.0:oob`
   6. Enable permissions: `/checkin` and `/scrobble`
   7. Click **Save App**
2. Copy the **Client ID** and **Client Secret** into CrossWatch.
3. Click **Connect Trakt**. A new tab opens.

![](/files/eJ3kw6uGlf1JxvJ23lU0)

4. Enter the **PIN** shown in CrossWatch (Link code). Click **Continue**.
5. Wait until you see:

```
TRAKT authorized. You can close this tab and return to the app.
```

### Troubleshooting

* If the auth tab doesn’t open, disable popup blockers for CrossWatch.
* If the PIN fails, generate a new link code and try again.
* If Trakt connects but sync fails, re-check that your app permissions include `/scrobble`.
* If you see `429 Too Many Requests`, tune provider throttling.\
  Guide: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries).
  {% endtab %}

{% tab title="Power users" %}

## Power users

### Config keys

Stored under:

* `trakt.*`

Common keys:

* `trakt.client_id`, `trakt.client_secret`
* `trakt.access_token`, `trakt.refresh_token`, `trakt.expires_at`

### Related docs

* Sync module: [Trakt module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/284be575c5a4659c13026f3002c4edbb0bd2f8a9)
* Adapter: [Adapter: Trakt](/crosswatch/settings/synchronization/trackers/trakt)
  {% endtab %}
  {% endtabs %}


# Others

Connect helper providers used for imports and utility workflows.

These providers don’t fit the “media server” or “tracker” bucket. They usually add utility features, imports, or extra data sources.

CrossWatch (CW) treats them as helpers, not sync targets.

{% hint style="info" %}
Use these providers to enrich or import data, not as your main source of truth.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### Available providers

<table data-view="cards"><thead><tr><th>Provider</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td><strong>Tautulli</strong><br>Import Plex history and playback insights into CW workflows.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/7DkCdHo0KFA1MgNMDdOD">/spaces/3rh5THg1PdhVsBt3GALo/pages/7DkCdHo0KFA1MgNMDdOD</a></td></tr></tbody></table>

### When to use these

* You need to import historical data (example: Plex history from Tautulli).
* You need an auxiliary service that CW can query, but not “sync” with.
  {% endtab %}

{% tab title="Power users" %}

### Pattern: import → local tracker → sync out

For imports, the safest pattern is:

1. import into [Adapter: CrossWatch](/crosswatch/settings/synchronization/others/cw)
2. validate results
3. sync from local tracker into other providers

{% hint style="success" %}
This pattern limits surprises. It gives you a checkpoint before external writes.
{% endhint %}
{% endtab %}
{% endtabs %}


# Tautulli

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/ISdk9LMtsRonVS4vwOWC" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Tautulli is **read-only** in CrossWatch. It’s meant for importing existing Plex history, not for syncing back into Plex.
{% endhint %}

### Connect Tautulli

1. Enter your **Server URL** and **API Key**.
   * Find the API key in Tautulli: **Settings → Web Interface → API**
2. Optional: set a **User ID**.
   * If you don’t set one, CrossWatch imports history for **all users**.
   * To find it:
     1. In Tautulli, open **Users**
     2. Click the user you want
     3. Check the browser URL: `/user?user_id=9999999`
3. Click **Connect** and wait until you see “Connected”.

### Troubleshooting

* Make sure the **Server URL** is reachable from the CrossWatch container.
* If imports are empty, verify the correct **User ID** (or remove it to import all users).
  {% endtab %}

{% tab title="Power users" %}

### Config keys

Stored under:

* `tautulli.server_url`
* `tautulli.api_key`
* `tautulli.user_id` (optional)

### Related docs

* Sync module: [Tautulli module](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/68281541dd94ac9aedcf339adc324bf706f6f3e4)
* Adapter: [Adapter: Tautulli](/crosswatch/settings/synchronization/others/tautulli)
  {% endtab %}
  {% endtabs %}


# Metadata

Configure TMDb metadata and Anime ID Mapping to improve matching and stabilize IDs across services.

Metadata and ID mapping help CrossWatch match the same item across different services.

Without them, CW sees more missing peers and weaker ID coverage.

That matters more now that Plex, Jellyfin, and Emby default to strict ID matching.

Find this section in **Settings → Connections**.

In older versions, this section was named **Metadata**.

<figure><img src="/files/uM8mWpwPq1aCWZsdNAPl" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Set up TMDb before your first real sync run.
{% endhint %}

### Why you want this section

Most matching problems start with weak identifiers:

* Different services use different IDs.
* Titles are localized, renamed, or ambiguous.
* Episodes can be especially messy without external IDs.

Metadata and ID mapping help CW:

* match items by stable IDs (TMDb/IMDb/etc. when available)
* reduce “missing peer” and “wrong match” issues
* make two-way sync less risky

{% hint style="info" %}
If you use AniList pairs, enable Anime ID Mapping too.
{% endhint %}

### What CW supports

CW supports:

* **TMDb** metadata
* **Anime ID Mapping** for AniList pairs

<table data-view="cards"><thead><tr><th>Guide</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td>TMDb Metadata</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/FWsYA6CO8FVG9KIb08pZ">/spaces/3rh5THg1PdhVsBt3GALo/pages/FWsYA6CO8FVG9KIb08pZ</a></td></tr><tr><td>Anime ID Mapping</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/K78pPkkhj5tsnPhNITkq">/spaces/3rh5THg1PdhVsBt3GALo/pages/K78pPkkhj5tsnPhNITkq</a></td></tr></tbody></table>

### Setup

{% stepper %}
{% step %}

### Open Metadata / ID Mapping

Go to **Settings → Connections**.
{% endstep %}

{% step %}

### Configure TMDb

Create a TMDb API key and paste it into CW.

Guide: [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata)
{% endstep %}

{% step %}

### Configure Anime ID Mapping

If you sync AniList with another provider, enable Anime ID Mapping.

Guide: [Anime ID Mapping](/crosswatch/settings/connections/metadata/anime-id-mapping)
{% endstep %}
{% endstepper %}

### Troubleshooting

* **Lots of missing matches**: configure TMDb, then re-run the pair.
* **AniList anime still missing peers**: enable Anime ID Mapping, then run the pair again.
* **Wrong match**: fix the item in the Editor (override IDs), then re-run.
* **Rate limits**: increase TMDb cache TTL and avoid rapid re-runs.


# TMDb Metadata

Configure TMDb metadata to improve matching and stabilize IDs across providers.

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/qU6lSQ4H93HTCTXVOPXY" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
CrossWatch uses TMDb for many features.

TMDb is strongly recommended if you want posters, backdrops, richer metadata, and more reliable matching.
{% endhint %}

### Setup

{% stepper %}
{% step %}

### Create a TMDb API key

1. Create a TMDb account.
2. Request an API key here: <https://www.themoviedb.org/settings/api>
   {% endstep %}

{% step %}

### Add the key to CW

1. Open **Settings → Connections**.
2. Paste your API key.
3. Click **Save**.
   {% endstep %}
   {% endstepper %}

{% hint style="info" %}
This key is used for matching only.

If you want TMDb watchlist/ratings syncing, connect **TMDb Sync** instead: [TMDb](/crosswatch/settings/connections/trackers/tmdb).
{% endhint %}

### Settings

<details>

<summary>Language / Locale</summary>

Controls the language used for titles, overviews, and other localized fields.

PublicMetaDB TMDb enrichment uses this setting too.

Pick a locale code from the list below.

</details>

<details>

<summary>Cache TTL (hours)</summary>

Controls how long CW caches posters and other TMDb payloads.

PublicMetaDB TMDb enrichment uses this cache too.

Default is `720` hours.

Higher TTL = fewer API calls.\
Lower TTL = fresher metadata.

</details>

### Supported locales

These locale codes control the language/region used for TMDb lookups.
{% endtab %}

{% tab title="Power users" %}

### Config keys

Metadata:

* `tmdb.api_key`
* `metadata.locale`
* `metadata.ttl_hours`

TMDb (Sync):

* `tmdb_sync.api_key`
* `tmdb_sync.session_id`

### Related docs

* Sync setup: [TMDb](/crosswatch/settings/connections/trackers/tmdb)
* Adapter: [Adapter: TMDb](/crosswatch/settings/synchronization/trackers/tmdb)
  {% endtab %}
  {% endtabs %}


# Anime ID Mapping

Match anime across services with different IDs and episode numbering.

Anime ID Mapping helps CrossWatch match anime across services with different IDs and episode numbering.

{% tabs %}
{% tab title="End users" %}

## Anime ID Mapping

Anime providers do not always describe the same show in the same way.

A normal TV provider might store an anime as one show with several seasons. An anime tracker may store each season as a separate title. It may use continuous episode numbers across the series.

CW Anime ID Mapping translates between those structures.

It can improve matching between providers such as:

* TMDB
* TVDB
* IMDb
* AniDB
* MyAnimeList
* AniList
* SIMKL
* Kitsu

It can also translate episode numbering when providers disagree about seasons and absolute episode numbers.

### What Anime ID Mapping does

CW uses Anime ID Mapping to:

* Add missing anime IDs to items
* Match the same anime across different provider ID systems
* Translate season and episode numbers into anime absolute episode numbers
* Translate anime absolute episode numbers back into normal season and episode numbers
* Handle anime where one provider uses a single show while another splits seasons into separate entries
* Apply custom mappings when the downloaded mapping does not match your setup

Custom rules always take priority over the downloaded dataset.

### Enable Anime ID Mapping

Open **Settings → Connections → Metadata → Anime ID Mapping**.

Enable Anime ID Mapping and save the settings.

When Anime ID Mapping is enabled for the first time, CW can download the mapping data and build its local index automatically.

Automatic updates can keep the local mapping data current.

### Using Anime Mapping in a sync pair

Anime ID Mapping is applied per sync feature.

Open your synchronization pair and look for **Use Anime ID Mapping** in the feature settings.

Watchlist and Ratings normally use Anime ID Mapping automatically when the pair supports it.

History mapping is opt in.

For supported AniList setups, **Anime only sync** can restrict that feature to anime items.

### Episode mapping

Episode mapping is useful when providers use different numbering models.

One provider may expose:

```
Season 1 Episode 13
```

The anime tracker may expect:

```
Episode 13
```

More complicated shows may have several normal seasons. The anime tracker may use one continuous episode sequence.

CW can translate those coordinates before writing the item.

It can also reverse that mapping when reading anime data back.

This is especially important with SIMKL. Some anime seasons use separate SIMKL entries and different TMDB IDs.

CW can map the SIMKL episode back to the original source show, season, and episode. The same item is then recognised on the next sync.

### Custom anime mappings

If the downloaded mapping does not match a title correctly, open **Custom anime mappings**.

A custom rule tells CW:

```
When the item is this ID
Treat it as this anime ID
```

For shows, you can optionally include:

* Source season
* First episode
* Last episode
* The episode number where the range starts on the anime tracker

Leave the episode fields empty when you only need to correct an ID.

Movies only use ID mapping. They do not have season or episode ranges.

Custom mappings can be enabled, disabled, edited, or deleted.

Your rules are evaluated before the downloaded Anime ID Mapping data.

### Find anime on SIMKL

When the target of a custom rule is **SIMKL**, CW can search SIMKL directly from the mapping editor.

A connected SIMKL account is required.

Select SIMKL as the target and use the search button.

Search by title. CW shows matching SIMKL anime entries.

For each result, you can choose **Use ID** or **Build seasons**.

**Use ID** fills the selected SIMKL ID. You can then finish the rule manually.

**Build seasons** helps when your source has one continuous season. SIMKL may split the anime into several entries.

CW follows related SIMKL season entries and prepares the episode ranges automatically.

CW shows the generated rules and asks for confirmation before saving.

Entries that have not aired, or lack a usable episode count, are skipped.

### Import custom mappings

Custom mappings can be imported from a CW JSON export.

Choose one of two modes:

**Merge into my rules**

Keeps your current rules and adds or updates rules from the imported file.

**Replace all rules**

Replaces your current custom rule set with the imported file.

CW validates imported rules before saving them.

Invalid rules are skipped. The import result shows added, updated, and skipped rules.

### Export custom mappings

Select **Export** to save all custom anime mapping rules as a JSON file.

The export contains your custom rules only.

It does not contain the downloaded Anime ID Mapping dataset.

Exports are useful for:

* Backing up custom mappings
* Moving mappings to another CW installation
* Sharing a known working mapping set
* Editing or reviewing a larger rule set outside CW

### When should I create a custom rule?

Create a custom rule when:

* The same anime keeps appearing as unresolved
* The wrong season or episode is written
* A SIMKL item is added again on every sync
* The provider uses a different ID than the downloaded mapping expects
* One provider splits a show differently from another provider
* You know the correct mapping and want CW to use it instead of the downloaded dataset

For normal titles, no custom rule is required.

Let the downloaded Anime ID Mapping handle them automatically.

### Troubleshooting

If mapping does not appear to work:

* Confirm Anime ID Mapping is enabled
* Confirm **Use Anime ID Mapping** is enabled for the feature in your sync pair
* Confirm the mapping index is installed and ready
* Update or rebuild the mapping index if required
* Check that the item has at least one usable provider ID
* Use a custom mapping when the automatic result is wrong

If SIMKL search is not available, connect SIMKL under tracker connections.
{% endtab %}

{% tab title="Power users" %}

## Power users

Anime ID Mapping is an ID enrichment and episode coordinate translation layer. It runs before provider-specific matching and writes.

It combines downloaded mapping data with local custom overrides.

Custom overrides have priority over the downloaded mapping.

### Data sources

CW builds the local anime index from two data sources.

The main relationship graph comes from **AniBridge**.

CW downloads:

```
stats.json
mappings.min.json
```

CW also downloads the anime identity dataset from **animeApi v3**.

The identity data provides an additional bridge. It helps when an item starts with an anime-specific ID, such as SIMKL or Kitsu. Those items may not have a normal mapping seed ID.

The downloaded data is converted into a local SQLite index for runtime lookups.

### Mapping model

The service supports two related operations:

1. Identity enrichment
2. Episode coordinate translation

Identity enrichment adds compatible IDs to an item.

Episode translation handles differences between normal season numbering and anime absolute numbering.

These operations are related but separate.

### Identity enrichment

Normal mapping seed namespaces include:

```
anilist
mal
anidb
tmdb
tvdb
imdb
```

SIMKL and Kitsu can also act as identity seeds. This applies when an item lacks a normal seed ID.

CW then uses the identity index to recover native mapping IDs. It continues through the normal mapping graph.

The mapping service follows edges to a limited depth. It only accepts an automatically selected ID when the result is unambiguous.

Existing IDs are not replaced merely because another mapping candidate exists.

### Custom override namespaces

A custom rule can match these providers:

```
tvdb
tmdb
imdb
anidb
mal
anilist
simkl
kitsu
```

A custom rule can target:

```
anidb
mal
anilist
simkl
kitsu
```

Supported media types are:

```
show
movie
```

Movie rules are identity only.

Show rules can additionally describe an episode range.

### Override precedence

For episode mapping, CW uses this order:

1. User override
2. AniBridge mapping
3. Native passthrough, when applicable

For reverse episode mapping, CW uses this order:

1. User override
2. AniBridge reverse mapping

A custom rule can correct both the write path and the readback path.

### Episode rule format

A show rule can contain:

```
match_provider
match_id
match_season
target_namespace
target_id
episode_from
episode_to
episode_start_at
```

Example concept:

```
TMDB 12345
Season 1
Episodes 13 to 24

→ SIMKL 67890
Episodes 1 to 12
```

The equivalent mapping values are:

```
episode_from: 13
episode_to: 24
episode_start_at: 1
```

The forward resolver translates:

```
S1E13 → episode 1
S1E14 → episode 2
S1E24 → episode 12
```

The reverse resolver translates the target coordinates back:

```
episode 1 → S1E13
episode 12 → S1E24
```

### Forward episode resolution

The episode resolver starts with source show IDs and season and episode coordinates.

TVDB and TMDB are preferred aired-numbering entry points.

When only TMDB is available, CW can use the local show-pair mapping. This reaches the related TVDB show.

The resolver checks the AniBridge season scope. It translates the source episode through stored source and target ranges.

Anime native targets currently prefer:

```
anidb
mal
anilist
```

Season zero is not automatically translated. The caller must explicitly allow specials.

### Reverse episode resolution

Reverse mapping lets anime tracker data be recognised when it is read back later.

The resolver accepts an anime target ID and an absolute episode number.

It first checks local user overrides.

If no override matches, it queries AniBridge reverse edges.

Reverse mapping can resolve back to:

```
tvdb
tmdb
```

A reverse result is only accepted when it produces one unambiguous season and episode coordinate.

This prevents ambiguous mappings from silently rewriting an item to the wrong episode.

### SIMKL readback

SIMKL is a main consumer of reverse episode mapping.

SIMKL may represent anime using:

* One flat absolute episode sequence
* Separate SIMKL entries for individual seasons
* Different TMDB IDs for those separate entries

When reading SIMKL anime history, CW can reverse the absolute episode number into the original aired coordinate.

Where required, source show IDs are also restored. The canonical item then matches the source provider again.

This fixes cases where a write succeeded on SIMKL. A later read could not recognise the item and attempted to add it again.

### SIMKL catalog lookup

The custom mapping editor exposes SIMKL catalog lookup through:

```
GET /api/anime-mapping/simkl/status
GET /api/anime-mapping/simkl/search
POST /api/anime-mapping/simkl/plan
```

A configured SIMKL profile is required.

If the requested profile is not configured, CW can resolve another configured SIMKL profile for the catalog client.

Search uses the SIMKL anime search API.

The current search result limit is capped at:

```
25
```

Only SIMKL anime entries with the TV anime type are returned.

Results can include:

```
SIMKL ID
title
display title
year
TMDB ID
episode count
poster
```

### Build seasons

**Build seasons** supports source seasons that represent several separate SIMKL anime entries.

CW starts from the selected SIMKL entry and inspects its sequel relationships.

Only aired entries with a usable year and positive episode count are included.

The entries are ordered and converted into contiguous source ranges.

For example, if the SIMKL chain contains:

```
Entry A, 12 episodes
Entry B, 12 episodes
Entry C, 10 episodes
```

CW can generate:

```
Source E1 to E12  → SIMKL A, E1 to E12
Source E13 to E24 → SIMKL B, E1 to E12
Source E25 to E34 → SIMKL C, E1 to E10
```

The generated plan is shown before the rules are created.

Unaired entries and entries without a usable episode count are reported as skipped.

### Import and export

Custom rules are stored independently from downloaded AniBridge data.

Export returns:

```json
{
  "overrides": [],
  "updated_at": 0
}
```

The generated filename uses:

```
cw-anime-mappings-<timestamp>.json
```

Import accepts either a JSON list of rules or an object containing an overrides list.

Supported modes are:

```
merge
replace
```

#### Merge

Merge starts with the current rule set.

Rules with an existing rule ID are updated.

New rule IDs are added.

Existing rules absent from the import remain untouched.

#### Replace

Replace starts with an empty rule set. It imports valid rules from the supplied file.

The UI asks for confirmation before replacing an existing rule set.

#### Validation

Every imported rule passes through the normal override validator.

Invalid entries are skipped.

The result reports:

```
received
imported
added
updated
skipped_count
total
```

Details for skipped rules are also returned. The response limits these to 50 skipped entries.

The current maximum is:

```
5000 custom rules
```

### Custom override storage

Custom mappings are stored at:

```
/config/.cw_state/anime_mapping_overrides.json
```

The file is separate from:

* Downloaded AniBridge mappings
* animeApi identity data
* The generated SQLite mapping index

Writes use atomic temporary-file replacement.

The runtime caches parsed overrides. It invalidates the cache after changes.

### Rule evaluation

Enabled rules are evaluated in stored order.

Disabled rules are ignored.

For episode rules, the first matching enabled range is used.

A show episode rule requires:

```
match_season
episode_from
episode_start_at
```

`episode_to` may be omitted for an open-ended range.

Episode numbers must be `1` or higher.

The ending episode cannot be below the starting episode.

Movie mappings cannot define season or episode coordinates.

### Pair behavior

Anime Mapping is globally enabled through the `anime_mapping` configuration block. Pair feature settings also control its use.

The normal default features are:

```
watchlist
ratings
```

History is an opt-in Anime Mapping feature.

Pair feature options include:

```
use_anime_mapping
anime_only_sync
```

`anime_only_sync` is disabled when `use_anime_mapping` is disabled.

By default, the global mapping scope is intended for pairs involving anime-native providers:

```
anilist
simkl
```

The configured provider list can also contain:

```
*
```

This allows Anime Mapping for any pair.

### Dataset updates

The default release tag is:

```
v3
```

When Anime ID Mapping is enabled and the local index is missing, saving the settings can bootstrap the dataset immediately.

Automatic updating is enabled by default when the feature is enabled.

The normal refresh interval is:

```
24 hours
```

The minimum interval is one hour.

CW first checks the AniBridge statistics metadata.

The full mapping file is downloaded and rebuilt only when required. A forced update bypasses this check.

The local index can also be rebuilt from downloaded mapping files. This does not download the dataset again.

### Local index

The runtime uses a generated SQLite index. It does not repeatedly parse downloaded mapping files.

Status information includes:

```
installed
index_ready
dataset_generated_on
age_hours
stale
source_count
edge_count
identity_count
last_checked_at
last_updated_at
index_built_at
```

The index can automatically rebuild when its schema is outdated.

### API

The main Anime Mapping API is:

```
/api/anime-mapping
```

Important endpoints include:

```
GET  /api/anime-mapping/status
POST /api/anime-mapping/settings
POST /api/anime-mapping/update
POST /api/anime-mapping/rebuild-index

GET    /api/anime-mapping/overrides
POST   /api/anime-mapping/overrides
DELETE /api/anime-mapping/overrides/{rule_id}

GET  /api/anime-mapping/overrides/export
POST /api/anime-mapping/overrides/import

GET  /api/anime-mapping/simkl/status
GET  /api/anime-mapping/simkl/search
POST /api/anime-mapping/simkl/plan
```

### Code map

Main mapping service:

```
cw_platform/anime_mapping/service.py
```

Episode translation and reverse translation:

```
cw_platform/anime_mapping/episodes.py
cw_platform/anime_mapping/coordinates.py
```

Custom mappings:

```
cw_platform/anime_mapping/overrides.py
```

SIMKL catalog lookup and season planning:

```
cw_platform/anime_mapping/simkl_catalog.py
```

Dataset updater:

```
cw_platform/anime_mapping/updater.py
cw_platform/anime_mapping/auto_update.py
```

API:

```
api/animeMappingAPI.py
```

UI:

```
assets/js/modals/anime-overrides/index.js
```

{% endtab %}
{% endtabs %}


# Profiles

Profiles let you use multiple accounts or servers for one provider (media servers, trackers, others)

{% hint style="info" %}
Start with the built-in **default** profile.

Add another profile only when you need another account or server.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### For most users

Use the **default** profile for each provider.

Set up a provider in **Settings → Connections**.

Choose the profile on each sync pair or Watcher route.

You do not need profile IDs or configuration files.

### When to add a profile

Add a profile when one provider needs separate connections.

Common examples:

* A Plex server at home and another remotely.
* A Trakt account for you and another for a partner.
* Jellyfin servers for separate libraries or households.

### Set up profiles in the UI

#### 1. Add the connection

Each profile stores its own connection details in **Settings → Connections**.

<figure><img src="/files/fhMVwQrwHuXooPkB3wI7" alt=""><figcaption></figcaption></figure>

Select **Profile** to create another connection.

Set its credentials, server URL, user, and provider options.

#### 2. Select profiles for sync pairs

Each side of a pair can use its own profile.

<figure><img src="/files/eY9p9DAO82KozQ3amAYZ" alt=""><figcaption></figcaption></figure>

#### 3. Select profiles for Watcher routes

Choose a profile for both the provider and the sink.

<figure><img src="/files/fZ3nZJ1bLt1BF88tvaeB" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Profiles are independent.

Set each profile's required token, URL, and provider options.
{% endhint %}

{% hint style="warning" %}
Pairs and Watcher routes can reference profiles directly.

Before deleting a profile, update each pair or route using it.

Otherwise runs can fail or use the wrong account or server.
{% endhint %}
{% endtab %}

{% tab title="Power users" %}
This section covers profile IDs, `config.json`, and API fields.

#### Profile IDs

* `default` is reserved.
* Extra profiles use `PROVIDER-P##`.
* Examples: `PLEX-P01`, `TRAKT-P01`, `SIMKL-P02`.

The **Profile** button always creates the next free ID.

If `PLEX-P01` already exists, the next profile is `PLEX-P02`.

#### Config and API field names

Use these fields when you edit `config.json` or call the API.

Each provider can define extra profiles in an `instances` map.

```json
{
  "plex": {
    "account_token": "...",
    "server_url": "http://plex.local:32400",
    "instances": {
      "PLEX-P01": {
        "account_token": "...",
        "server_url": "https://remote-plex.example"
      }
    }
  }
}
```

Pairs use:

* `source_instance`
* `target_instance`

Both default to `default`.

```json
{
  "pairs": [
    {
      "enabled": true,
      "source": "PLEX",
      "source_instance": "PLEX-P01",
      "target": "TRAKT",
      "target_instance": "TRAKT-P01",
      "mode": "one-way",
      "features": { "watchlist": true, "ratings": false, "history": true, "playlists": false }
    }
  ]
}
```

Watcher routes use `provider_instance` and `sink_instance`.

Each route is:

`provider(profile) → sink(profile)`

```json
{
  "scrobble": {
    "watch": {
      "routes": [
        {
          "id": "R1",
          "enabled": true,
          "provider": "plex",
          "provider_instance": "default",
          "sink": "trakt",
          "sink_instance": "TRAKT-P01",
          "filters": {}
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Related topics

* Connect providers: [Authentication](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/XXSROEx2Yqy98k1SqJvO)
* Configure sync pairs: [Configure Pairs](/crosswatch/settings/configure-pairs)
* Configure real-time scrobbling: [Watcher](/crosswatch/settings/scrobbler/watcher)
* Scope media-server libraries per profile: [Library Whitelisting](/crosswatch/settings/connections/library-whitelisting)


# Library Whitelisting

Limit which Plex/Jellyfin/Emby libraries CrossWatch reads, writes, or scrobbles.

Control which Plex, Jellyfin, and Emby libraries CrossWatch reads, writes, synchronizes, and scrobbles.

Whitelisting reduces unnecessary processing and prevents CrossWatch from operating on libraries that are outside the intended sync scope.

{% hint style="info" %}
An empty whitelist disables filtering for that category.

CrossWatch allows all libraries visible to the configured user.

CrossWatch only blocks other libraries after you select one or more libraries.
{% endhint %}

Use whitelisting to:

* Reduce API calls
* Improve sync performance
* Exclude unrelated libraries, such as music, photos, or live TV
* Limit History, Ratings, and Progress synchronization
* Prevent scrobbling from selected libraries

<figure><img src="/files/9cnUl2kosLXauy2HiG66" alt=""><figcaption></figcaption></figure>

### Whitelist levels

CrossWatch supports two whitelist levels.

#### Server level whitelisting

Server level whitelisting is configured in the provider authentication settings.

It applies to every pair that uses the selected provider profile.

Server level whitelisting is available for:

* History, H
* Ratings, R
* Progress, P
* Scrobble, S

The server level whitelist defines the maximum scope available to CrossWatch.

#### Pair level whitelisting

Pair level whitelisting is configured in the pair editor.

It applies only to the selected pair and is available for:

* History
* Ratings
* Progress

Pair level whitelisting lets you reduce the scope of an individual pair without changing other pairs that use the same provider profile.

Scrobble whitelisting is only available at server level because scrobbling is event driven and is not processed through sync pairs.

### How the levels interact

Server level whitelisting defines the maximum permitted scope.

Pair level whitelisting defines the subset used by a specific pair.

For example, when the server level History whitelist contains Movies and TV Shows, the pair editor only offers Movies and TV Shows for History.

A pair cannot select a library that is excluded by the server level whitelist.

When the server level whitelist for a feature is empty, filtering is disabled for that feature.

All visible libraries remain available to pairs.

When one or more libraries are selected, only those libraries are allowed and the rest are blocked.

### Recommended configuration

Use server level whitelisting for the default scope of a provider profile.

Use pair level whitelisting when an individual pair requires a smaller scope.

Example:

Server level History:

* Movies
* TV Shows

Pair A History:

* Movies

Pair B History:

* TV Shows

Both pairs remain within the maximum server level scope.

### Server level whitelisting

Server level whitelisting is available for Plex, Jellyfin, and Emby under:

**Settings → Connections → Authentication → Provider → Whitelisting**

When Profiles, also called Instances, are enabled, whitelisting is configured separately for each profile.

Each profile has its own server connection, user, and library selections.

#### Configure server level whitelisting

{% stepper %}
{% step %}

### Open the provider

Go to **Settings → Connections → Authentication**.

Open **Plex**, **Jellyfin**, or **Emby**.
{% endstep %}

{% step %}

### Select a profile

Select the provider profile you want to configure.

Whitelists belong to the selected profile and do not affect other profiles.
{% endstep %}

{% step %}

### Open Whitelisting

Open the **Whitelisting** section.
{% endstep %}

{% step %}

### Load libraries

Select **Load Libraries**.

CrossWatch retrieves the libraries available to the configured server and user.

Emby and Jellyfin expose libraries as views.

CrossWatch resolves media items to their corresponding view before applying the whitelist.
{% endstep %}

{% step %}

### Select libraries

Select the libraries allowed for each feature:

* H, History
* R, Ratings
* P, Progress
* S, Scrobble
  {% endstep %}

{% step %}

### Save

Save the provider settings.

The selected libraries become the maximum available scope for all pairs using this provider profile.
{% endstep %}
{% endstepper %}

### Pair level whitelisting

Pair level whitelisting is available in the pair editor for Plex, Jellyfin, and Emby.

Pair selections are stored per feature and per provider.

#### Configure pair level whitelisting

{% stepper %}
{% step %}

### Open the pair

Go to **Pairs** and edit the required pair.
{% endstep %}

{% step %}

### Open a feature

Open **History**, **Ratings**, or **Progress**.
{% endstep %}

{% step %}

### Load libraries

Open the Plex, Jellyfin, or Emby provider settings in the pair editor.

Select **Load Libraries** when the libraries have not already loaded automatically.

CrossWatch only displays libraries permitted by the server level whitelist for the selected feature.
{% endstep %}

{% step %}

### Select libraries

Select the libraries that this pair may use for the current feature.

Library selections are independent for History, Ratings, and Progress.
{% endstep %}

{% step %}

### Save

Save the pair and run it again.
{% endstep %}
{% endstepper %}

### Progress whitelisting

Progress whitelisting controls which libraries participate in resume position synchronization.

It can be configured at both levels:

* Server level, defines the maximum Progress scope for a provider profile
* Pair level, defines the Progress scope for one pair

Progress selections are independent from History selections.

Selecting a library for History does not automatically select it for Progress.

### Scrobble whitelisting

Scrobble whitelisting controls which libraries can generate real time scrobble events.

It applies to:

* Watchers
* Webhooks

Scrobble filtering is allow list based.

When the Scrobble whitelist is empty, scrobble filtering is disabled.

CrossWatch accepts events from all visible libraries.

When one or more libraries are selected, CrossWatch only accepts events that can be resolved to one of those library identifiers.

Events from other libraries are blocked.

When a whitelist is configured but an event does not contain a usable library identifier, CrossWatch skips the event.

There is no separate scrobble library blacklist.

### Dashboards and Insights

Some dashboards use orchestrator state rather than the final per pair adapter scope.

Server level whitelisting is therefore recommended for global filtering.

Pair level filtering can make some dashboard totals appear larger than the data actually processed by an individual pair.

Use server level whitelisting when dashboard scope must match the overall sync scope.

### Troubleshooting

#### The pair editor shows fewer libraries than expected

Check the server level whitelist for the same provider profile and feature.

The pair editor only displays libraries permitted by the server level whitelist.

Clear the server level selection for that feature to make all visible libraries available.

#### Pair level whitelisting is not applied

Confirm that libraries are selected under the correct provider and feature.

History, Ratings, and Progress have separate library selections.

Save the pair and run it again.

#### Progress synchronization includes the wrong library

Check both the server level Progress whitelist and the pair level Progress whitelist.

Do not rely on the History whitelist, because History and Progress use separate selections.

#### Scrobbling ignores the pair selection

This is expected.

Scrobble whitelisting is configured only at server level.

Verify the S selection under the provider profile Whitelisting settings.

#### Scrobble events are skipped

When a Scrobble whitelist is configured, CrossWatch must resolve the event to an allowed library.

Events without a usable library identifier are rejected.

Check the watcher or webhook logs for library resolution details.

#### Insights shows too much data

Insights may use orchestrator state before pair level adapter filtering is applied.

Use server level whitelisting for global scope and pair level whitelisting only for pair specific exceptions.

### Reset whitelisting

To allow all visible libraries again:

1. Open the provider profile under **Settings → Connections → Authentication**.
2. Clear the selected H, R, P, and S libraries.
3. Save the provider settings.
4. Open each affected pair.
5. Clear its History, Ratings, and Progress library selections.
6. Save and run the pair again.

An empty whitelist means filtering is disabled and all visible libraries are allowed.

### Related topics

* [Profiles](/crosswatch/settings/connections/profiles)
* [Pair-level Whitelisting](/crosswatch/settings/configure-pairs/provider-settings/pair-level-whitelisting)
* [Filters](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/PH2jfcZjseHr6sFkoKAP)
* [Progress](/crosswatch/settings/configure-pairs/features/progress)


# Synchronization

Adapter reference for how CW reads/writes provider data during syncing.

Synchronization adapters are the “connectors” the orchestrator uses for syncing. Each adapter knows how to read a provider and how to write changes back.

Related: [CW Orchestrator](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/458ZbsIKcwSYg9y6eb8R).

{% hint style="success" %}
If a provider behaves unexpectedly, start with its adapter page.
{% endhint %}

{% hint style="info" %}
Start with one pair and one feature. Expand only after the first run is clean.
{% endhint %}

### How to use this section

{% stepper %}
{% step %}

### Connect providers

Use [Authentication](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/XXSROEx2Yqy98k1SqJvO).
{% endstep %}

{% step %}

### Build a pair

Create one pair and enable one feature.
{% endstep %}

{% step %}

### Check adapter behavior

If a provider acts oddly, open its adapter page.
{% endstep %}
{% endstepper %}

### What adapters do

Adapters implement the same core flow:

* **Index**: fetch a present-state snapshot (what exists now).
* **Normalize**: turn provider rows into common items + stable IDs.
* **Apply**: add/remove/update on the target side.
* **Stabilize**: use local state to avoid repeats and flapping.

### Feature semantics (how to think about “changes”)

* **Watchlist**: set-like. Adds and removes are straightforward.
* **History**: event-like. `watched_at` matters. Duplicates are common.
* **Ratings**: numeric. Scale differences matter. Deletes can be destructive.
* **Playlists**: ordered. Often provider-specific. Not widely supported yet.

### Stability knobs (common patterns)

* Snapshot caching to reduce API load.
* Chunking + small pauses to avoid rate limits.
* Shadow/unresolved files to prevent endless retries.
* Activity/watermark logic for delta providers (example: SIMKL).

### Adapter guides

<table data-view="cards"><thead><tr><th>Adapter</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td>Plex</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/OWAe360aTxSHXAnIiRoW">/spaces/3rh5THg1PdhVsBt3GALo/pages/OWAe360aTxSHXAnIiRoW</a></td></tr><tr><td>Jellyfin</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/LVxifNNcUFwNIcZzn53r">/spaces/3rh5THg1PdhVsBt3GALo/pages/LVxifNNcUFwNIcZzn53r</a></td></tr><tr><td>Emby</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/cHaK7elcPSgvo1VR2E48">/spaces/3rh5THg1PdhVsBt3GALo/pages/cHaK7elcPSgvo1VR2E48</a></td></tr><tr><td>Nuvio</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/FvjpLwNX009lgZoh3kcm">/spaces/3rh5THg1PdhVsBt3GALo/pages/FvjpLwNX009lgZoh3kcm</a></td></tr><tr><td>Trakt</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/bRhLnM3fO7uDpCByQXcs">/spaces/3rh5THg1PdhVsBt3GALo/pages/bRhLnM3fO7uDpCByQXcs</a></td></tr><tr><td>SIMKL</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/eqXoLy9ij0AlCf3sbYdg">/spaces/3rh5THg1PdhVsBt3GALo/pages/eqXoLy9ij0AlCf3sbYdg</a></td></tr><tr><td>MDBList</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/CsNqeI2L3uOOXg95AW06">/spaces/3rh5THg1PdhVsBt3GALo/pages/CsNqeI2L3uOOXg95AW06</a></td></tr><tr><td>PublicMetaDB</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/IgNgLWcDb9O2aYI59Nax">/spaces/3rh5THg1PdhVsBt3GALo/pages/IgNgLWcDb9O2aYI59Nax</a></td></tr><tr><td>AniList</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/GUWl5kpWjOzbTroJ9EC0">/spaces/3rh5THg1PdhVsBt3GALo/pages/GUWl5kpWjOzbTroJ9EC0</a></td></tr><tr><td>Tautulli</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/7R7JVYBKElUh131kNRvA">/spaces/3rh5THg1PdhVsBt3GALo/pages/7R7JVYBKElUh131kNRvA</a></td></tr><tr><td>CW local backup</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/wo1bvQ75vjpmwdLU5SbB">/spaces/3rh5THg1PdhVsBt3GALo/pages/wo1bvQ75vjpmwdLU5SbB</a></td></tr></tbody></table>

### Troubleshooting quick checks

* **Adds keep reappearing**: provider didn’t “stick” the write. Check adapter state files.
* **Recent changes not syncing**: reduce caching, or force a fresh read if supported.
* **429 / 5xx errors**: reduce chunk size, add delay, run less often.
* **Mismatches**: add [Metadata / ID Mapping](/crosswatch/settings/connections/metadata) and prefer stable IDs.

### Related topics

* [Configure Pairs](/crosswatch/settings/configure-pairs) — step-by-step pair setup.
* [Best practices](/getting-started/best-practices) — safe defaults.


# Media servers

Adapter reference for Plex, Jellyfin, and Emby.

Adapter reference for Plex, Jellyfin, and Emby.


# Emby

Emby adapter notes for syncing watchlist/history/ratings and handling ID/matching quirks.

Emby adapter lets CrossWatch treat Emby as a sync provider in pairs. It supports watchlist, history, and progress. It prefers external IDs for matching.

### What it supports

* Direction: one-way or two-way (depends on the pair)
* Features:
  * **Watchlist** (favorites, playlist, or collection mode)
  * **History** (mark played, read played)
  * **Progress** (resume position)
  * **Ratings** (not supported yet)
  * **Playlists** (not supported yet)
* Indexing: present-state snapshot (reads “what exists now”)

{% hint style="info" %}
Connect Emby first. Use: [Emby (Authentication provider)](/crosswatch/settings/connections/media-servers/emby).
{% endhint %}

### How matching works

CrossWatch normalizes items into a common shape:

* `type`, `title`, `year`, `ids`
* Episodes also include `series_title`, `season`, `episode`

IDs come from Emby `ProviderIds` when present (IMDb/TMDb/TVDb). The Emby internal item ID is also stored as `ids.emby`.

{% hint style="warning" %}
Strict ID matching is the default.

If external IDs are missing, CrossWatch usually treats the item as a missing peer instead of guessing by title/year.

You can relax this per pair, but false positives get more likely, especially for episodes.
{% endhint %}

### Watchlist behavior

Pick one watchlist mode:

1. **Favorites** (default)\
   Uses the user `IsFavorite` flag for Movies and Series.
2. **Playlist**\
   Uses a playlist (default name: `Watchlist`) as the watchlist container.
3. **Collection**\
   Uses a named collection as the watchlist container.

Reads enumerate the chosen container. Writes toggle favorites or add/remove items in the playlist/collection.

{% hint style="warning" %}
Avoid **Playlist** mode unless you need it. It’s easier to accidentally create duplicates and churn.
{% endhint %}

### History behavior

Reads recent plays for:

* Movies
* Episodes

Each play becomes a normalized event with `watched_at` in UTC (`...Z`).\
Writes mark items played, with optional backdating.

### Progress behavior

* Read: current resume position (“Continue Watching”).
* Write: set resume position.
* Clear: supported (writes progress `0`).

Related: [Progress](/crosswatch/settings/configure-pairs/features/progress).

### Settings (advanced)

The UI shows these in **Synchronization** on the pair that uses Emby.

<details>

<summary>Important config keys</summary>

Top-level:

* `server`, `access_token`, `user_id`, `device_id`
* `timeout`, `max_retries`, `verify_ssl`

Watchlist:

* `mode`: `favorites` | `playlist` | `collection`
* `playlist_name`
* `watchlist_query_limit`, `watchlist_write_delay_ms`
* `watchlist_guid_priority`

History:

* `history_query_limit`, `history_write_delay_ms`
* `history_guid_priority`
* `libraries` (optional scoping)
* `force_overwrite`, `backdate`, `backdate_tolerance_s`

Ratings (future):

* `ratings_query_limit`, `libraries`
* `ratings_like_threshold`

</details>

### Diagnostics

<details>

<summary>Health checks</summary>

* Ping: `GET /System/Ping`
* System info: `GET /System/Info`
* User probe: `GET /Users/{userId}`

Logs are prefixed with `[EMBY:feature]` (example: `[EMBY:watchlist] ...`).

</details>

<details>

<summary>State and unresolved files</summary>

* Watchlist unresolved: `/config/.cw_state/emby_watchlist.unresolved.json`
* History unresolved: `/config/.cw_state/emby_history.unresolved.json`
* Ratings unresolved: `/config/.cw_state/emby_ratings.unresolved.json`
* History shadow (merge-only): `/config/.cw_state/emby_history.shadow.json`
* Health shadow: `/config/.cw_state/emby.health.shadow.json`

These prevent infinite retries and make failures visible.

</details>

### Notes and limitations

* Prefer libraries with good external IDs.
* If writes are flaky, add a small write delay.
* Emby actions are user-scoped. Use separate pairs per Emby user.


# Jellyfin

Jellyfin adapter notes for syncing watchlist/history/ratings and handling missing external IDs.

Jellyfin adapter lets CrossWatch sync with your Jellyfin server. It supports watchlist, history, and progress. It prefers external IDs for matching.

{% hint style="warning" %}
CrossWatch requires **Jellyfin 10.9 or newer**.

Older Jellyfin versions are not supported.
{% endhint %}

### What it supports

* Direction: source or target in a pair (one-way or two-way)
* Features:
  * **Watchlist** (favorites, playlist, or collection mode)
  * **History** (played movies and episodes)
  * **Progress** (resume position)
  * **Playlists** (not supported yet)
* Indexing: present-state snapshot (reads “what exists now”)

{% hint style="info" %}
Connect Jellyfin first. Use: [Jellyfin (Authentication provider)](/crosswatch/settings/connections/media-servers/jellyfin).
{% endhint %}

### How matching works

CrossWatch normalizes items into a common shape:

* `type`, `title`, `year`, `ids`
* Episodes can include `show_ids`, `season`, `episode`

IDs are pulled from Jellyfin `ProviderIds` when available (IMDb/TMDb/TVDb). For episodes, the adapter tries to include show context in `show_ids`.

{% hint style="warning" %}
Strict ID matching is the default.

If external IDs are missing, CrossWatch usually treats the item as a missing peer instead of guessing by title/year.

You can relax this per pair, but false positives get more likely, especially for episodes.
{% endhint %}

### Watchlist behavior

Pick one watchlist mode:

1. **Favorites** (default)\
   Uses the user `IsFavorite` flag for Movies and Series.
2. **Playlist**\
   Uses a playlist (default name: `Watchlist`) as the watchlist container.
3. **Collection**\
   Uses a named collection as the watchlist container.

Reads enumerate the chosen container. Writes toggle favorites or add/remove from the playlist/collection.

{% hint style="info" %}
Recommended: **Favorites** or **Collection**.
{% endhint %}

{% hint style="warning" %}
Jellyfin playlists can’t hold Series. Playlist mode will only cover Movies and Episodes.
{% endhint %}

### History behavior

* Reads played Movies and Episodes, sorted by `DateLastPlayed`.
* Each entry becomes a normalized event with `watched_at` in UTC (`...Z`).

{% hint style="info" %}
Jellyfin does not expose unique play-event IDs. CrossWatch approximates event identity using item key + timestamp.
{% endhint %}

### Progress behavior

* Read: current resume position (“Continue Watching”).
* Write: set resume position.
* Clear: supported (writes progress `0`).

Related: [Progress](/crosswatch/settings/configure-pairs/features/progress).

### Settings (advanced)

The UI shows these in **Synchronization** on the pair that uses Jellyfin.

<details>

<summary>Important config keys</summary>

Watchlist:

* `mode`: `favorites` | `playlist` | `collection`
* `playlist_name`
* `watchlist_query_limit`, `watchlist_write_delay_ms`
* `watchlist_guid_priority`

History:

* `history_query_limit`, `history_write_delay_ms`
* `history_guid_priority`
* `history_libraries` (optional include-list of library IDs)

</details>

### Diagnostics

<details>

<summary>Health checks</summary>

* Ping: `GET /System/Ping`
* System info: `GET /System/Info`
* User probe: `GET /Users/{userId}`

Logs are prefixed with `[JELLYFIN:feature]` (example: `[JELLYFIN:history] ...`).

</details>

<details>

<summary>State and unresolved files</summary>

* History shadow (merge-only): managed by the module/orchestrator

These prevent infinite retries and make failures visible.

</details>

### Notes and limitations

* Missing external IDs reduces match quality.
* If writes are flaky, add a small write delay.
* Jellyfin actions are user-scoped. Use separate pairs per Jellyfin user.


# Plex

Plex adapter notes for syncing watchlist/history/ratings with stable external IDs.

Plex adapter lets CrossWatch (CW) sync with Plex. It supports watchlist, history, ratings, and progress. It prefers stable external IDs.

{% hint style="info" %}
Connect Plex first. Use: [Plex (Authentication provider)](/crosswatch/settings/connections/media-servers/plex).
{% endhint %}

### What it supports

* Direction: source or target in a pair (one-way or two-way)
* Features:
  * **Watchlist** (via Plex Discover)
  * **History** (via Plex Media Server)
  * **Ratings** (via Plex Media Server)
  * **Progress** (resume position)
  * **Playlists** (not supported)
* Indexing: present-state snapshot (reads “what exists now”)

### How matching works

CW tries to match by provider IDs first.

* Watchlist: GUID / external IDs from Plex Discover
* History + ratings: PMS items enriched with external IDs when available

{% hint style="warning" %}
Strict ID matching is the default.

If external IDs are missing, CrossWatch usually prefers a missing peer over a wrong match.

Enable the experimental fallback only if you accept the trade-offs.
{% endhint %}

### Watchlist behavior

* Read: pulls your watchlist from **Plex Discover**.
* Write: add/remove via Discover actions.
* No fuzzy title matching.

### History behavior

* Read: PMS play history for the selected user.
* Write:
  * Add = mark watched (Plex uses current server time)
  * Remove = unscrobble

{% hint style="warning" %}
Plex has no supported way to **backdate** a play.

If CrossWatch writes **History** into Plex, Plex records `watched_at` as **now**.

This affects history sync and **History** capture restores.

That matters because when you later syncs history back from Plex to a tracker, CrossWatch send those Plex timestamps back to the tracker as if they were the real watch dates. This can overwrite the user’s historical watch timeline.
{% endhint %}

{% hint style="info" %}
Plex has “play history” and “marked watched”. Marked watched is optional and add-only. See below.
{% endhint %}

### Ratings behavior

* Read: scans libraries and keeps items with `userRating`.
* Write:
  * Add = rate (1–10)
  * Remove = clear rating (rating 0)

### Progress behavior

* Read: current resume position for items with progress.
* Write: set resume position (“Continue Watching”).
* Clear: not supported by Plex.

Related: [Progress](/crosswatch/settings/configure-pairs/features/progress).

### Friend and shared account behavior

When you select a Plex friend or shared user from the server owner’s profile, CrossWatch can try to use Plex’s server-scoped shared token for that user.

Support is limited:

* **History** — supported
* **Progress** — supported
* **Ratings** — not supported
* **Watchlist** — not supported
* **Playlists** — not supported

Ratings are blocked for shared users because Plex can return the server owner’s ratings instead of the shared user’s ratings.

Watchlist is blocked because Plex watchlist lives in account-level cloud data.

{% hint style="warning" %}
Use the owner’s Plex profile for shared-user history and progress only.

Use the friend’s own Plex login if you need watchlist or ratings.
{% endhint %}

### Settings (advanced)

<details>

<summary>Common keys and knobs</summary>

Workers:

* `plex.rating_workers` (1–64)
* `plex.history_workers` (1–64)

Library filters:

* `plex.ratings.libraries` (section IDs)
* `plex.history.libraries` (section IDs)

Watchlist:

* `plex.watchlist_query_limit`
* `plex.watchlist_write_delay_ms`
* `plex.watchlist_allow_pms_fallback`

History:

* `plex.history_ignore_local_guid`
* `plex.history_ignore_guid_prefixes` (example: `local://`)
* `plex.history_require_external_ids`
* `plex.history.include_marked_watched`

Experimental:

* `plex.fallback_GUID`

</details>

<details>

<summary>Marked Watched (Plex “checkmark”) behavior</summary>

Plex has two different signals:

* **Play history**: real play events
* **Marked watched**: manual “mark as watched” state

If `plex.history.include_marked_watched = true`:

* CW also scans your libraries for items Plex considers watched
* It merges those into history **only if Plex provides a timestamp** (`lastViewedAt` / `viewedAt`)

This is **add-only**:

* Mark watched in Plex → can sync out
* Unmark watched in Plex → CW will **not** unwatch on other services

{% hint style="warning" %}
Marked Watched is intended for the PMS owner. it will automatically be disabled when used for home users or friends.
{% endhint %}

</details>

<details>

<summary>Experimental: GUID fallback</summary>

Fallback tries to recover IDs when PMS hydration fails (404) or IDs are missing.

Enable with `plex.fallback_GUID = true`.

The main purpose is to preserve old data once. Then you’re done.

In practice:

1. Create a temporary **Plex → provider X** pair.
2. Enable **Fallback GUID**.
3. Run sync to move the old data to provider X.
4. Remove that pair again.
5. Clean up everything.
6. Disable **Fallback GUID** in your next pair.

{% hint style="warning" %}
Enable this only temporarily.

Run it once, then disable it.

It costs extra API calls and CPU time.

It can slow runs and trigger timeouts or rate limits.
{% endhint %}

Used for:

* History rows that can’t be hydrated by `ratingKey`
* Rated items missing external IDs

Strategy (best-effort):

1. Query Plex metadata service for external IDs
2. For episodes, also hydrate the show via `grandparentRatingKey`
3. Use Discover title+year search as a last resort

To keep it fast and quiet, results are memoized:

* `/config/.cw_state/plex_fallback_memo.json`

Delete the file to force retries.

</details>

### Diagnostics

<details>

<summary>Unresolved (freeze) files</summary>

Items that can’t be matched or written are frozen to avoid repeat retries:

* Watchlist: `/config/.cw_state/plex_watchlist.unresolved.json`
* History: `/config/.cw_state/plex_history.unresolved.json`
* Ratings: `/config/.cw_state/plex_ratings.unresolved.json`

</details>

### Notes and limitations

* Prefer external IDs and stable libraries.
* If `plex.baseurl` uses `https://`, the certificate must validate correctly from the CrossWatch container. Invalid or rejected certificates can cause `HTTPSConnectionPool` or `SSLError` failures.
* Friend/shared account support is best-effort.
* Shared-user support depends on Plex’s server-scoped token behavior and may change in the future.
* Selecting a friend from the owner’s Plex profile does not grant access to that friend’s personal watchlist or ratings data.
* Setup details: [Auth: Plex](/crosswatch/settings/connections/media-servers/plex).
* Start one-way. Run one feature. Then expand.


# Media clients

Adapter reference for supported media clients.

Media clients connect directly to CrossWatch as synchronization targets.

{% hint style="danger" %}

### Media-client sync warning

Media clients are best used as sync destinations.

Nuvio, Kodi, and Stremio are playback and library clients. They are not perfect history authorities. When used as a source, some data can be incomplete or approximate. Some data uses when CW first observed a change.

This matters most for history dates:

* Kodi uses `lastplayed` for watched history. Ratings and resume progress lack event timestamps. CrossWatch tracks when it first sees or changes those values.
* Nuvio provides watched and progress timestamps when available. Items without required dates are skipped or marked unresolved.
* Stremio stores exact watched dates for movies, but not individual episodes. Episode watched state syncs, but exact episode dates cannot be recovered.

For the safest setup, sync one-way from a provider to the media client. Only use media-client-to-provider or two-way sync if incomplete or approximate dates can sync back to providers.

Start with **Dry run**. Enable one feature at a time. Keep removals disabled until the plan looks correct.
{% endhint %}

### Supported clients

#### Nuvio

Nuvio synchronizes its Library, watched history, and resume progress. It does not support ratings or playlists.

Nuvio is an experimental adapter. API changes can temporarily affect synchronization.

Before creating a pair:

1. Connect Nuvio under **Settings** → **Connections** → **Clients**.
2. Approve the temporary login code and select a Nuvio profile.
3. Configure TMDb metadata for stronger identity matching.

Use one-way synchronization and **Dry run** for your first pair. Enable one feature at a time. Keep removals disabled until you verify the plan.

For feature support, matching, removal behavior, and troubleshooting, see [Nuvio](/crosswatch/settings/synchronization/media-clients/nuvio).

#### Kodi

Kodi synchronizes watched history, ratings, and resume progress for movies and episodes. It also works as a Watcher playback source.

Kodi is an experimental adapter. It requires Kodi 21 Omega, JSON-RPC 13.5.0 or newer, and authenticated HTTP remote control.

Use source-path whitelists to limit History, Ratings, Progress, and Watcher activity. Kodi writes only to items already in its video library.

Use one-way synchronization and **Dry run** for your first pair. Enable one feature at a time. Keep removals disabled until you verify the plan.

For feature support, matching, whitelist behavior, and troubleshooting, see [Kodi](/crosswatch/settings/synchronization/media-clients/kodi).

#### Stremio

Stremio synchronizes History, Progress, Watchlist, and supported Ratings.

Stremio is experimental and uses its internal account API. API changes can temporarily affect synchronization.

Stremio uses its Library as the Watchlist. It keeps episode watched state without individual watched dates. Ratings write to Stremio only as Liked or Loved reactions.

Use one-way synchronization and **Dry run** for your first pair. Enable one feature at a time. Keep removals disabled until you verify the plan.

For feature support, matching, data behavior, and troubleshooting, see [Stremio](/crosswatch/settings/synchronization/media-clients/stremio).

### Supported features

| Client  | History        | Ratings               | Progress       | Watchlist      |
| ------- | -------------- | --------------------- | -------------- | -------------- |
| Kodi    | Read and write | Read and write        | Read and write | Not supported  |
| Nuvio   | Read and write | Not supported         | Read and write | Read and write |
| Stremio | Read and write | Write to Stremio only | Read and write | Read and write |

History and Progress support movies and individual episodes. Watchlists support movies and shows. Seasons are not synchronized separately.

{% hint style="danger" %}
Media clients can store less detail than trackers or media servers. Start with one-way sync, then run **Dry run** before writing data.
{% endhint %}

### What you can lose

| Client  | Key limitation                                                                                                                                 |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Kodi    | Separate plays collapse into one play count and latest date. Decimal ratings are rounded. Missing library items cannot be added.               |
| Nuvio   | Repeated plays become one History record. Progress needs a position, duration, and timestamp. Episodes and seasons cannot enter the Watchlist. |
| Stremio | Episode watched dates are lost. Numeric Ratings become reactions. Only one unfinished episode per series retains Progress.                     |

{% hint style="info" %}
Keep a tracker as the source of truth when exact dates, Ratings, or several active episodes matter.
{% endhint %}

### Data handling reference

Support does not guarantee full fidelity. Some clients retain the latest state only. Others retain dated events. Review these limits before configuring a pair.

#### What can be lost or changed

| Client  | Feature         | When used as source                                          | When used as destination                                              | What you may lose                                         |
| ------- | --------------- | ------------------------------------------------------------ | --------------------------------------------------------------------- | --------------------------------------------------------- |
| Kodi    | History         | CW reads watched state, play count, and `lastplayed`.        | CW writes the play count and latest date to an existing library item. | Separate play events.                                     |
| Kodi    | Ratings         | Kodi provides a Rating from 1 to 10.                         | Kodi accepts an integer Rating from 1 to 10.                          | Decimal Ratings are rounded.                              |
| Kodi    | Progress        | Kodi provides resume position and duration.                  | CW writes resume position to an existing library item.                | The original Progress timestamp is unavailable.           |
| Kodi    | Watchlist       | Not available.                                               | Not available.                                                        | Watchlists cannot synchronize through Kodi.               |
| Nuvio   | History         | Nuvio provides a watched date for movies and episodes.       | Nuvio accepts the source watched date.                                | Separate repeated plays are not preserved.                |
| Nuvio   | Ratings         | Not available.                                               | Not available.                                                        | Ratings cannot synchronize through Nuvio.                 |
| Nuvio   | Progress        | Nuvio provides position, duration, and a Progress timestamp. | All three values are required for writes.                             | Progress without duration or timestamp cannot be written. |
| Nuvio   | Watchlist       | The Nuvio Library is the Watchlist.                          | Movies and shows are added to the Nuvio Library.                      | Episodes and seasons are unsupported.                     |
| Stremio | Movie History   | Stremio provides the latest watched date and watched state.  | Stremio stores one latest watched date.                               | Separate historical play events are not preserved.        |
| Stremio | Episode History | Stremio stores watched episodes in a bitfield.               | Stremio stores only watched state.                                    | Every individual episode watched date.                    |
| Stremio | Ratings         | Stremio cannot be a Ratings source.                          | Ratings become Stremio reactions.                                     | The original numeric Rating and Rating date.              |
| Stremio | Progress        | A movie can expose its current resume position.              | Stremio requires position and duration.                               | Progress without duration cannot be written.              |
| Stremio | Progress        | A series record exposes one active episode.                  | Writing another episode replaces the active episode.                  | Multiple unfinished episodes from one series.             |
| Stremio | Watchlist       | The Stremio Library is the Watchlist.                        | Movies and shows are added to the Library.                            | There is no separate Watchlist.                           |
| Stremio | All features    | Matching normally requires IMDb.                             | TMDb metadata can resolve IMDb when possible.                         | Items without a usable mapping remain unresolved.         |

#### Important History differences

**Exact History versus watched state**

Trackers can store dated History events. Media clients usually store current watched state, with no date or only one latest date.

When a client does not provide the original date, CW cannot recreate it. The destination uses its import date, another fallback, or leaves the item unresolved.

**Multiple watches**

Kodi, Nuvio, and Stremio synchronize as one current History record per movie or episode.

A tracker can contain several separate plays. When tracker History synchronizes to a media client, those plays can collapse into one watched state.

A later sync back to the tracker cannot restore the original events.

#### Common pair examples

| Pair                        | What is synchronized                                         | Main risk                                                                                           |
| --------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| Stremio to Plex             | Movie and episode watched state                              | Stremio episodes have no individual watched date. Plex can mark them watched using the import date. |
| Plex to Stremio             | Movie History, episode watched state, and supported Progress | Individual Plex episode dates are discarded in Stremio.                                             |
| Stremio to Trakt            | Movie dates and episode watched state                        | Original episode dates are unavailable.                                                             |
| Trakt to Stremio            | Movie and episode watched state                              | Separate Trakt plays collapse into one state. Episode dates are not retained.                       |
| Provider Ratings to Stremio | Movie and show Ratings                                       | Numeric Ratings become `liked` or `loved`. Ratings below the threshold create no reaction.          |
| Kodi to a tracker           | Watched state, latest date, Ratings, and Progress            | Missing Kodi dates and synthetic timestamps are not historical source data.                         |
| Tracker to Kodi             | History, Ratings, and Progress for matching library items    | Missing watched dates become the current date. Decimal Ratings are rounded.                         |
| Nuvio to a tracker          | Watched dates and Progress from the selected profile         | Repeated plays are represented as one current item.                                                 |
| Tracker to Nuvio            | History, Progress, and Watchlist                             | High Progress can be treated as completed.                                                          |
| Any provider to Stremio     | Supported data with Stremio-compatible identifiers           | Episode dates, exact Ratings, and multiple episode Progress entries can be lost.                    |

#### Two-way sync risks

Two-way sync can send reduced data back to the provider that originally contained more detail.

For example, a tracker can send exact episode dates to Stremio. Stremio stores watched state only. A later Stremio-to-tracker sync cannot recover those dates.

The same applies to numeric Ratings converted to reactions and several episode Progress entries reduced to one active Stremio episode.

Two-way sync is not recommended when a client stores less information.

{% hint style="warning" %}
Do not use Stremio as a Ratings source. Keep exact episode dates and Ratings authoritative elsewhere.
{% endhint %}


# Kodi

Kodi synchronization reference.

Synchronize history, ratings and playback progress between Kodi and other supported CW providers.

The Kodi sync adapter connects directly to the Kodi video library through HTTP JSON RPC.

{% hint style="warning" %}
**Experimental**

The adapter is experimental and requires Kodi 21 Omega or newer.

**Media-client sync warning:** Review [Media clients](/crosswatch/settings/synchronization/media-clients) before using a media client as a source or enabling two-way sync.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### What it supports

1. History, read, add, update and remove
2. Ratings, read, add, update and remove
3. Progress, read, add, update and remove
4. Movies
5. Episodes
6. One way synchronization
7. Two way synchronization
8. Separate source whitelisting for each feature

Kodi does not support watchlist or playlist synchronization.

Shows and seasons are not synchronized as separate items.

### Prerequisite

Connect Kodi under **Settings** → **Connections** → **Media clients** before creating a synchronization pair.

CW must show the Kodi connection as connected.

The Kodi video library must already contain the movies and episodes you want to synchronize.

CW updates existing Kodi library entries. It does not add media files, scan folders or create new Kodi library entries.

### Supported features

#### History

CW reads the Kodi play count and last played timestamp for movies and episodes.

Kodi returns `lastplayed` as a local timestamp without a time zone.

CW treats it as local time on the host running the CW Kodi adapter.

CW converts it to UTC internally and back to Kodi-local time when writing history.

Adding history marks the matching Kodi item as watched and writes the available watched timestamp.

Removing history clears the play count and last played timestamp.

History synchronization requires a safe match with an existing Kodi library item.

#### Ratings

CW reads and writes the Kodi user rating.

Kodi uses a rating scale from 1 to 10.

Ratings have no timestamps. CW synchronizes numeric values only.

Removing a rating resets the Kodi user rating to 0.

Ratings are supported for movies and episodes.

#### Progress

CW reads and writes the Kodi resume position for movies and episodes.

Progress contains the current playback position and the total duration when available.

Kodi does not provide a reliable watched or resumed timestamp.

CW timestamps progress in UTC when it first observes or changes during synchronization.

Removing progress resets the resume position to the beginning.

### Whitelisting

Kodi whitelisting is configured under the Kodi connection.

Different video source paths can be selected for:

1. History
2. Ratings
3. Progress

An empty selection includes all Kodi video sources.

A selected source path also includes media stored below that path.

Whitelisting applies when Kodi is used as either the source or the target of a pair.

### Configure a pair

1. Open **Settings** → **Synchronization**.
2. Create a new pair.
3. Select Kodi as the source or target.
4. Select the correct Kodi Profile.
5. Select the other provider and Profile.
6. Start with One way mode.
7. Enable one supported feature.
8. Keep **Dry run** enabled for the first run.
9. Save the configuration.
10. Run the pair and review unresolved items before enabling writes.

Only features supported by both providers are shown.

For example, Progress is available only when the other provider also supports Progress.

### Direction recommendations

#### Kodi to tracker

Use Kodi as the source when Kodi contains the history or ratings you want to send to a tracker.

A common starting configuration is:

1. Mode, One way
2. Source, Kodi
3. Target, Trakt, SIMKL or MDBList
4. Feature, History or Ratings

For new playback activity, Watcher is usually more suitable than repeatedly synchronizing the entire history.

#### Tracker to Kodi

Use Kodi as the target when you want to apply tracker history or ratings to items in your Kodi library.

The corresponding movie or episode must already exist in Kodi and must be matched safely.

Items missing from the Kodi library remain unresolved.

#### Kodi to media server

Kodi can synchronize supported features with another media server when both adapters support the feature.

Use this carefully. Libraries on both sides should contain the same content and accurate identifiers.

Start with One way mode and enable removals only after validating several clean runs.

### Removals

Kodi supports removals for History, Ratings and Progress.

A removal is applied only when all relevant removal controls are enabled.

This includes:

1. Global removal settings
2. The removal setting for the pair feature
3. The configured synchronization direction

Do not enable removals during the first run.

Removing data from Kodi changes the corresponding fields in the Kodi database.

### Troubleshooting

#### Items remain unresolved

Confirm that the movie or episode exists in the Kodi video library.

Check whether Kodi contains an IMDb, TMDb or TVDb identifier for the item.

For episodes, check the series identifier, season number and episode number.

Ambiguous title matches are intentionally not written.

#### No features are available

A feature is shown only when both providers support it.

Kodi supports History, Ratings and Progress.

The selected target or source may not support the same feature.

#### No Kodi items are found

Check the Kodi connection.

Confirm that the correct Kodi Profile is selected.

Review the Kodi source whitelist. An incorrect path selection can exclude the entire library.

An empty whitelist includes all video sources.

#### Changes are not written

Confirm that **Dry run** is disabled.

Confirm that Add or Update is enabled for the feature.

For removals, confirm that removal is enabled globally and for the pair feature.

Review the run details for unresolved or ambiguous items.

### Summary

Kodi provides bidirectional synchronization for History, Ratings and Progress.

Only movies and episodes already present in the Kodi video library can be updated.

Start with One way mode, one feature and Dry run. Review matching results before enabling additional features, two way synchronization or removals.
{% endtab %}

{% tab title="Power users" %}

### How matching works

CW first tries to match Kodi library items using external identifiers.

Supported identifiers include:

1. IMDb
2. TMDb
3. TVDb

Episodes are matched using the show identifier together with the season and episode number.

When the source item contains external identifiers, CW does not fall back to a title match if those identifiers cannot be found in Kodi.

When no external identifier is available, CW may use the title and year. The result must be unique.

Ambiguous matches remain unresolved. CW will not update an uncertain Kodi library item.

Accurate identifiers in the Kodi library are important for reliable synchronization.

### Multiple Kodi clients

Kodi supports multiple CW Profiles.

Each Profile represents a separate Kodi client and stores its own:

1. Server URL
2. Credentials
3. SSL setting
4. History whitelist
5. Ratings whitelist
6. Progress whitelist

Select the correct Profile in each pair.

### Limitations

1. The adapter is experimental.
2. Kodi 21 Omega or newer is required.
3. Only movies and episodes are supported.
4. Watchlists are not supported.
5. Playlists are not supported.
6. Shows and seasons are not synchronized as separate items.
7. Media must already exist in the Kodi video library.
8. Matching depends on the metadata and external identifiers stored by Kodi.
9. Kodi progress does not include a reliable native modification timestamp.
10. Kodi must remain reachable while the synchronization runs.
    {% endtab %}
    {% endtabs %}


# Nuvio

Nuvio synchronization reference.

Synchronize the Nuvio Library, watched history, and resume progress with other supported CrossWatch providers.

Nuvio is a separate CrossWatch sync provider. It is not a traditional media server or tracker. You can select it as either source or target in a pair.

{% hint style="warning" %}
**Experimental**

The Nuvio sync adapter is experimental. Nuvio API contracts may change and temporarily affect synchronization.

**Media-client sync warning:** Review [Media clients](/crosswatch/settings/synchronization/media-clients) before using a media client as a source or enabling two-way sync.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### What Nuvio can synchronize

The Nuvio sync adapter supports:

1. Watchlist
2. History
3. Progress

Ratings and playlists are not supported.

### Supported media types

#### Watchlist

Watchlist synchronization supports movies and shows.

Nuvio does not provide a separate watchlist. CrossWatch uses the Nuvio Library as the Nuvio watchlist.

Adding an item to the CrossWatch watchlist adds it to the Nuvio Library. Removing an item removes it from the Nuvio Library.

Seasons and episodes cannot be added through the Watchlist feature.

#### History

History synchronization supports movies and episodes.

Shows and seasons cannot be synchronized as watched items. For episodes, CrossWatch uses the show identity, season number, and episode number.

#### Progress

Progress synchronization supports movies and episodes.

Shows and seasons cannot have their own progress records. Progress requires both a playback position and the item’s full duration.

### Before creating a pair

1. Open **Settings** → **Connections** → **Clients**.
2. Connect Nuvio.
3. Approve the temporary Nuvio login code.
4. Select the Nuvio profile CrossWatch should use.
5. Save the connection.
6. Configure TMDb under **Settings** → **Metadata**.

TMDb metadata is strongly recommended when Nuvio is a target. It converts IMDb and TVDb identities into Nuvio’s common TMDb identities.

### Create a Nuvio pair

Open **Settings** → **Synchronization**, then create a new pair. Nuvio can be selected on either side.

#### Nuvio as source

Use Nuvio as the source when it provides the authoritative state.

Examples:

1. Nuvio Library to Trakt Watchlist.
2. Nuvio watched history to Plex.
3. Nuvio resume progress to Jellyfin.

In a one-way pair, CrossWatch applies Nuvio changes to the target. Target-only changes are not copied back.

#### Nuvio as target

Use Nuvio as the target when another provider controls the Nuvio state.

Examples:

1. Trakt Watchlist to the Nuvio Library.
2. Plex watched history to Nuvio.
3. Emby resume progress to Nuvio.

When **Watchlist Remove** is enabled, removing a source item can remove it from the Nuvio Library.

#### Two-way synchronization

Nuvio supports two-way synchronization. CrossWatch reads both providers and applies its standard merge and conflict rules.

Start with one-way synchronization. Use two-way synchronization only after validating matching and removal behavior.

### Recommended first pair

1. Set **Mode** to **One way**.
2. Enable one feature only.
3. Enable **Add**.
4. Keep **Remove** disabled.
5. Enable **Dry run**.
6. Run the pair manually.
7. Review the plan and unresolved items.
8. Disable **Dry run** after confirming the result.

Test features in this order:

1. Watchlist
2. History
3. Progress

### Watchlist behavior

CrossWatch reads the complete Nuvio Library when building its watchlist snapshot.

When writing changes, CrossWatch preserves existing Library entries and available metadata. It reads the library again afterward and confirms each requested change.

Do not change the Nuvio Library during an active Watchlist run. Library updates replace the complete library and simultaneous changes can conflict.

### History behavior

CrossWatch reads watched movie and episode records from the selected Nuvio profile.

Each watched item needs a valid watched timestamp. CrossWatch skips writes when Nuvio already has the same or newer timestamp.

When **History Remove** is enabled, CrossWatch can remove movie and episode history. This removes the watched state, not the Library title.

### Progress behavior

CrossWatch reads Nuvio’s resume position, duration, and last watched timestamp.

A progress write requires:

1. A supported movie or episode identity.
2. A valid playback position.
3. A valid duration.
4. A valid progress timestamp.

CrossWatch compares source and target timestamps before updating. It can skip writes when the target has the same or newer progress state.

You can also manage these records in **Continue Watching**. It uses the same Nuvio progress data.

### Removal safety

**Remove** is disabled by default in most CrossWatch configurations.

Removal requires permission from:

1. The global synchronization settings.
2. The pair’s feature settings.
3. The selected synchronization mode.

Start with **Add** enabled and **Remove** disabled. Take extra care with Watchlist removals because the CrossWatch Watchlist represents the complete Nuvio Library.

### Matching requirements

Nuvio works best with TMDb and IMDb identities. CrossWatch can process TMDb, IMDb, and TVDb IDs.

A direct TMDb identity is preferred. IMDb and TVDb identities may need configured TMDb metadata before CrossWatch can write them to Nuvio.

Items without a usable identity appear as unresolved and are not written.

### Troubleshooting

#### Nuvio is unavailable in the pair selector

Confirm that Nuvio is connected and that you selected a Nuvio profile. Both authentication and profile selection are required.

#### The pair reports `profile_unavailable`

The selected Nuvio profile no longer exists or cannot be returned. Open the Nuvio connection and select another profile.

#### Watchlist items are unresolved

Check for a TMDb, IMDb, or TVDb ID. Configure TMDb metadata when the source provides only IMDb or TVDb.

#### Episode history is unresolved

Confirm CrossWatch has:

1. A usable show identity.
2. A season number.
3. An episode number.

This adapter does not expose absolute episode or anime episode mappings.

#### Progress reports `duration_missing`

The source lacks a usable runtime or duration. CrossWatch cannot calculate or write resume progress without it.

#### Changes repeat during every run

Confirm that Nuvio returned the written item during verification. Also check for missing IDs, profile changes, API availability issues, or manual changes during synchronization.

#### Nuvio service unavailable

Retry the run. An experimental adapter may require a newer CrossWatch release after a Nuvio API change.
{% endtab %}

{% tab title="Power users" %}

### Module overview

| Item             | Value                                |
| ---------------- | ------------------------------------ |
| Provider name    | `NUVIO`                              |
| Module           | `providers/sync/_mod_NUVIO.py`       |
| Watchlist module | `providers/sync/nuvio/_watchlist.py` |
| History module   | `providers/sync/nuvio/_history.py`   |
| Progress module  | `providers/sync/nuvio/_progress.py`  |
| Shared logic     | `providers/sync/nuvio/_common.py`    |

### Provider contract

The Nuvio module implements the standard CrossWatch sync provider interface:

1. `health()`
2. `build_index(feature)`
3. `add(feature, items)`
4. `remove(feature, items)`

The module is bidirectional, experimental, read-after-write verified, and able to provide canonical external IDs.

`index_semantics` is `present`. Each Nuvio index represents current provider state, not a delta.

### Feature manifest

#### Watchlist

* **Enabled:** `true`
* **Types:** movies and shows.
* **Unsupported:** seasons and episodes.
* **Operations:** read, add, remove, and observed deletes.

#### History

* **Enabled:** `true`
* **Types:** movies and episodes.
* **Unsupported:** shows and seasons.
* **Operations:** read, add, remove, and observed deletes.

#### Progress

* **Enabled:** `true`
* **Types:** movies and episodes.
* **Unsupported:** shows and seasons.
* **Operations:** read, add or update, remove, and observed deletes.

Progress writes require duration.

#### Ratings and playlists

Ratings and playlists are disabled: `false`.

### Nuvio RPC endpoints

The adapter uses authenticated RPC requests.

| Purpose               | Endpoint                                                                             |
| --------------------- | ------------------------------------------------------------------------------------ |
| Profile validation    | `sync_pull_profiles`                                                                 |
| Watchlist and Library | `sync_pull_library`, `sync_push_library`                                             |
| History               | `sync_pull_watched_items`, `sync_push_watched_items`, `sync_delete_watched_items`    |
| Progress              | `sync_pull_watch_progress`, `sync_push_watch_progress`, `sync_delete_watch_progress` |

All requests use the selected Nuvio profile ID.

### Watchlist implementation

The Watchlist feature operates on the complete Nuvio Library.

Read flow:

1. Pull Library pages from Nuvio.
2. Normalize movies and series.
3. Convert identities into canonical CrossWatch keys.
4. Build a present-state index.

Write flow:

1. Pull the current Library.
2. Resolve requested items.
3. Merge additions with existing rows.
4. Preserve available Nuvio metadata.
5. Remove permitted items.
6. Push the complete resulting Library.
7. Pull the Library again.
8. Verify each requested change.

A lock prevents concurrent Library writes for the same CrossWatch and Nuvio profiles. It cannot prevent external Nuvio changes.

### Library metadata

Existing Nuvio Library metadata is preserved where possible. New entries can use configured TMDb metadata.

Supported enrichment includes name, poster, background, description, release information, and genres.

### Content identity

Nuvio content IDs currently use formats such as `tmdb:550` and `tt0137523`.

CrossWatch prefers TMDb for writes. For non-episode items, it checks:

1. Existing Nuvio content identity.
2. Direct TMDb identity.
3. Direct IMDb identity.
4. TMDb metadata resolution from IMDb or TVDb.

Episodes use the show identity, content ID, season number, episode number, and video ID. Without an existing video ID, CrossWatch creates a canonical identifier from the content ID, season, and episode.

### History implementation

History uses paginated `sync_pull_watched_items` calls. The default internal page size is 900. The maximum accepted value is 1000.

Rows are rejected without a supported identity, watched timestamp, or valid episode numbering. For duplicate canonical records, CrossWatch retains the newest watched timestamp.

CrossWatch rebuilds the Nuvio history index after writes to verify them.

### Progress implementation

Progress uses `sync_pull_watch_progress`. Pagination uses `last_watched` as a cursor.

A valid row requires a content ID, video ID, position, duration, and last watched timestamp. CrossWatch calculates percentage from position and duration.

Before writing, the standard progress policy compares source and target timestamps, positions, and durations. After writing, it confirms the resulting position and timestamp.

### Pagination

* Library reads use a maximum page size of 500.
* History reads use a maximum page size of 1000.
* Progress reads use a maximum page size of 1000.

Pagination stops when a page has fewer records than requested. Invalid responses fail the feature read rather than returning a partial snapshot.

### Configuration namespace

The default Nuvio connection is stored under `nuvio.*`.

| Key                   | Purpose                                           |
| --------------------- | ------------------------------------------------- |
| `nuvio.base_url`      | API base URL. Defaults to `https://api.nuvio.tv`. |
| `nuvio.access_token`  | Current access token.                             |
| `nuvio.refresh_token` | Refresh token.                                    |
| `nuvio.expires_at`    | Access-token expiry timestamp.                    |
| `nuvio.profile_id`    | Selected Nuvio profile ID.                        |
| `nuvio.profile_name`  | Selected profile display name.                    |

The adapter has no separate feature-tuning keys. Authentication, profile selection, pair settings, and global synchronization controls determine behavior.

### Provider profiles

Additional CrossWatch provider profiles can maintain independent Nuvio connections. Each stores separate tokens, profile selection, and connection state.

Pairs select a CrossWatch provider profile through `source_instance` or `target_instance`.

Do not confuse the CrossWatch provider profile with the Nuvio viewer profile. The provider profile identifies the complete connection. The Nuvio profile ID identifies the viewer within that account.

### Health check

The health check validates:

1. Authentication exists.
2. A Nuvio profile is selected.
3. The selected profile remains available.
4. Progress, history, and Library data can be queried.

It does not intentionally write provider data.

Possible statuses:

`not_configured`, `ok`, `token_refresh_failed`, `auth_failed`, `profile_unavailable`, `invalid_response`, and `service_unavailable`.

### Verification behavior

The adapter enables read-after-write verification for supported features. An API success response alone is not confirmation.

CrossWatch confirms Library additions and removals, history additions and removals, progress positions and timestamps, and progress removals. Failed verification returns an unresolved or failed operation.

### Removal semantics

The module reports observed-delete support for Watchlist, History, and Progress. Actual removal still depends on standard CrossWatch safety controls:

1. Global remove enablement.
2. Pair feature removal settings.
3. One-way removal mode.
4. Drop guard.
5. Mass-delete protection.
6. Observed-delete processing.

The Nuvio module does not bypass orchestrator safety controls.

### Current technical limitations

1. The API integration is experimental.
2. Ratings and playlists are unavailable.
3. Watchlist uses and replaces the complete merged Nuvio Library payload.
4. History and progress do not support standalone shows or seasons.
5. Progress requires duration.
6. Anime-specific and absolute episode numbering are unavailable.
7. Items without a resolvable TMDb or IMDb identity cannot be written.
8. IMDb and TVDb input may require TMDb metadata resolution.
9. Nuvio is not a real-time Watcher source or tracker scrobble target.
   {% endtab %}
   {% endtabs %}


# Stremio

Stremio synchronization reference.

Synchronize Stremio History, Progress, Watchlist, and supported Ratings with other CrossWatch providers.

Stremio is a separate CrossWatch sync provider. You can select it as either source or target in a pair.

For connection instructions, account profiles, and connection troubleshooting, see [Stremio](/crosswatch/settings/connections/media-clients/stremio).

{% hint style="warning" %}
**Experimental**

The adapter uses Stremio’s internal account API. Stremio changes can affect the connection or supported features.

**Media-client sync warning:** Review [Media clients](/crosswatch/settings/synchronization/media-clients) before using Stremio as a source or enabling two-way sync.
{% endhint %}

{% tabs %}
{% tab title="End users" %}

### What Stremio can synchronize

The Stremio sync adapter supports:

1. History
2. Progress
3. Watchlist
4. Ratings to Stremio only

Playlists are not supported.

### Supported media types

#### History

History synchronization supports movies and episodes.

Shows and seasons cannot be synchronized as separate History items.

#### Progress

Progress synchronization supports movies and episodes.

Shows and seasons cannot have separate Progress records.

Stremio keeps one active episode Progress entry per series. Writing Progress for another episode can replace it.

#### Watchlist

Watchlist synchronization supports movies and shows.

CrossWatch uses the Stremio Library as the Stremio Watchlist. Episodes and seasons are not supported.

#### Ratings

Ratings synchronization supports movies and shows only. Stremio is a destination only.

CrossWatch converts numeric Ratings into Stremio reactions. By default:

* Ratings below `6` create no reaction.
* Ratings from `6` up to `8` become **Liked**.
* Ratings from `8` up to `10` become **Loved**.

Stremio does not store the original numeric value.

### Before creating a pair

1. Connect Stremio from [Stremio](/crosswatch/settings/connections/media-clients/stremio).
2. Confirm the Stremio connection shows as connected.
3. Configure TMDb metadata under **Settings** → **Connections** → **Metadata**.

A separate Stremio application or addon is not required.

Stremio items need a usable IMDb identifier. TMDb metadata can resolve missing identifiers, posters, and durations when possible.

### Create a Stremio pair

Open **Settings** → **Synchronization**, then create a new pair. Stremio can be selected on either side.

#### Stremio as source

Use Stremio as the source for its current History, Progress, or Library items.

A common starting configuration is:

1. Set **Mode** to **One way**.
2. Select Stremio as the source.
3. Select a tracker or media server as the target.
4. Enable History, Progress, or Watchlist.

Stremio cannot be used as a Ratings source.

Episode History does not contain individual watched dates. The target may receive watched state without its original timestamp.

#### Stremio as target

Use Stremio as the target to apply tracker or media-server data.

A common starting configuration is:

1. Set **Mode** to **One way**.
2. Select another provider as the source.
3. Select Stremio as the target.
4. Enable History, Progress, Watchlist, or Ratings.

Movie History can retain the latest available watched timestamp. Episode History retains watched state, but not its original timestamp.

#### Two-way synchronization

Stremio supports two-way synchronization for History, Progress, and Watchlist.

Ratings are not available in two-way mode. Stremio cannot be a Ratings source.

Stremio can store less information than another provider. For example, episode History retains watched state but not individual watched dates.

Start with one-way synchronization. Enable two-way sync only after several clean runs.

### Recommended first pair

1. Set **Mode** to **One way**.
2. Enable one feature only.
3. Keep **Remove** disabled.
4. Enable **Dry run**.
5. Run the pair manually.
6. Review unresolved items.
7. Disable **Dry run** after confirming the result.

### History behavior

CrossWatch reads and writes Stremio History for movies and episodes.

For movies, Stremio stores watched state, a latest watched timestamp, and a watched count. CrossWatch synchronizes the current state and latest available timestamp.

Stremio does not provide separate movie play events.

For episodes, Stremio stores watched state without individual watched timestamps. CrossWatch can synchronize that an episode was watched, but not when it was watched.

Removing History marks the matching movie or episode as unwatched.

{% hint style="warning" %}
Use Stremio carefully as a History source when watched dates matter.
{% endhint %}

### Progress behavior

CrossWatch reads and writes Stremio playback Progress for movies and episodes.

Progress includes a playback position and total duration. A duration is required when writing Progress to Stremio.

TMDb metadata can resolve a missing duration. For series, Stremio stores one active episode Progress entry. Writing another episode can replace the previous entry.

Removing Progress resets the playback position. Progress does not automatically mark an item as watched.

### Watchlist behavior

CrossWatch uses Stremio Library membership as Watchlist state.

Adding a Watchlist item adds it to the Stremio Library. Removing it removes the item from that Library.

History and Library membership are separate. A watched title can remain in the Library and return as a Watchlist item.

### Whitelisting and removals

Stremio does not provide separate feature whitelisting. Use pair rules to restrict synchronized items.

Stremio supports removals for History, Progress, Watchlist, and destination Ratings. A removal is applied only when global, pair-feature, and direction controls permit it.

Do not enable removals during your first run.

* History removal marks an item as unwatched.
* Progress removal clears its playback position.
* Watchlist removal removes the item from the Stremio Library.

A Watchlist removal does not remove History. A History removal does not remove the item from the Library.

### Troubleshooting

#### Items remain unresolved

Confirm that the source item has an IMDb identifier. Configure TMDb metadata when one is unavailable.

For episodes, confirm the show identifier, season number, and episode number. Specials and alternative episode orders can remain unresolved.

#### No features are available

A feature appears only when both providers support it in the selected direction.

Stremio supports History, Progress, and Watchlist in both directions. Ratings appear only when Stremio is the target.

#### Ratings are not written

Confirm that Stremio is the target and that the source supports Ratings.

Ratings below the configured Liked threshold create no reaction. Episode Ratings are not supported.

#### Episode watched dates are missing or changed

Stremio does not store individual episode watched timestamps. CrossWatch synchronizes watched state, but cannot recover unavailable dates.

Do not use Stremio as the History source when exact episode dates must be preserved.

#### Progress is not written

Confirm that the item has a playback position and duration. Percentage-only Progress requires CrossWatch to resolve a duration.

Configure TMDb metadata when the source does not provide one.

#### Previous episode Progress disappeared

Stremio stores one active episode Progress entry per series. Writing Progress for another episode replaces the active entry.

#### Unexpected Watchlist items

CrossWatch uses the Stremio Library as the Watchlist. Watched titles remain included.

Remove unwanted titles from the Library or restrict them through pair rules.

#### Changes are not written

Confirm that **Dry run** is disabled and that **Add** or **Update** is enabled.

For removals, confirm global and pair-feature removal settings. Review run details for unresolved items and missing identifiers.
{% endtab %}

{% tab title="Power users" %}

### How matching works

CrossWatch primarily matches Stremio items using IMDb identifiers.

* Movies require a movie IMDb identifier.
* Episodes require the show IMDb identifier, season number, and episode number.
* Watchlist and Ratings support movies and shows.

History and Progress support movies and episodes.

When IMDb is missing, configured TMDb metadata can resolve one from external identifiers, title, and year.

Episode matching also uses the Stremio and Cinemeta episode list. The destination episode must use compatible season and episode numbering.

### Stremio History data

#### Movies

Movie History uses:

```
state.lastWatched
state.timesWatched
state.flaggedWatched
```

A movie is watched when `timesWatched` or `flaggedWatched` exceeds zero. `lastWatched` contains the latest available movie timestamp.

Adding movie History sets watched state and the latest timestamp. Removing it clears watched state, count, and timestamp.

#### Episodes

Episode History uses:

```
state.watched
```

This serialized watched bitfield records which episodes are watched. It has no individual episode timestamps.

CrossWatch does not use the Stremio Library modification timestamp as an episode watched timestamp. Library, metadata, or Progress updates can change it.

Adding or removing episode History updates only the matching episode in the bitfield. Other watched episodes remain unchanged.

### Stremio Progress data

Progress uses:

```
state.timeOffset
state.duration
state.video_id
state.season
state.episode
```

Movie records contain a position and duration. Series records contain one active episode through `video_id`, with one position and duration.

Progress writes read, merge, and write the Stremio record. CrossWatch preserves unrelated History, Library, and metadata fields.

The adapter has no automatic completion policy. High Progress does not automatically create History.

### Stremio Watchlist data

CrossWatch treats Stremio Library membership as Watchlist state. An item is listed when:

```
removed = false
temp = false
```

Watchlist supports movies and shows. Watched state does not exclude an item from the Library.

Removing a Watchlist item changes Library membership only. It does not clear watched state or Progress.

### Stremio Ratings data

Ratings are written through the Stremio reaction service. Supported values are:

```
liked
loved
```

CrossWatch converts numeric Ratings using configurable thresholds. The defaults are:

```json
{
  "stremio": {
    "ratings": {
      "liked_min": 6.0,
      "loved_min": 8.0
    }
  }
}
```

`liked_min` sets the lowest Rating that becomes Liked. `loved_min` sets the lowest Rating that becomes Loved.

Ratings below `liked_min` are skipped. When `loved_min` is below `liked_min`, CrossWatch uses the Liked threshold as the effective Loved threshold.

CrossWatch keeps pair-scoped local state for reactions it writes. This supports bookkeeping and duplicate prevention. It does not make Stremio a Ratings source.

### Multiple Stremio accounts

Stremio does not expose account subprofiles through this adapter. CrossWatch supports separate connection Profiles.

Each CrossWatch Profile represents one Stremio account and stores its own auth key. Select the correct Profile in each pair.

Data is not shared between Stremio connection Profiles.

### Authentication

CrossWatch connects with the Stremio email address and password. Stremio returns an auth key.

CrossWatch stores the returned `authKey` as `auth_key`. It does not retain the email address or password after connecting.

The auth key is encrypted in the CrossWatch configuration. The adapter uses Stremio’s internal account API, not the public addon API.

### Metadata enrichment

IMDb is the primary Stremio identifier. Configured TMDb metadata can resolve:

* A missing IMDb identifier.
* A missing poster.
* A missing runtime or duration.

For episode Progress, CrossWatch can retrieve the runtime from TMDb. Metadata enrichment does not guarantee a match.

Alternative episode orders, specials, and incorrect provider mappings can remain unresolved.

### Removal semantics

History, Progress, and Watchlist expose present state. When removal synchronization is enabled, CrossWatch can apply a missing state as a removal.

Ratings removal is available only when Stremio is the destination.

Do not enable observed removals until the initial pair state is reviewed. The Stremio Library can contain watched titles and titles added for other reasons.

### Limitations

1. The adapter is experimental and uses an internal Stremio API.
2. Ratings are destination only and reduced to Liked or Loved.
3. Episode History has no individual watched timestamps.
4. Movie History has no complete list of separate play events.
5. One active episode Progress entry is available per series.
6. The Stremio Library is used as the Watchlist.
7. Feature whitelisting and playlists are unavailable.
8. Reliable sync needs IMDb identifiers and compatible episode numbering.
9. Stremio and Cinemeta must remain reachable during synchronization.

### Related docs

* [Stremio](/crosswatch/settings/connections/media-clients/stremio)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata)
  {% endtab %}
  {% endtabs %}


# Trackers

Adapter reference for tracker services.

Adapter reference for tracker services.


# AniList

Sync anime watchlists and ratings with AniList.

AniList sync is built for **anime watchlists and ratings**. It syncs AniList’s **Planning** list, supports AniList ratings, and tries to keep IDs usable elsewhere.

{% hint style="warning" %}
CrossWatch is optimized for **movies and shows**. Anime works well for common titles. Edge cases can still fail matching.
{% endhint %}

### What it supports

* Features: **Watchlist** and **Ratings**
* List used: **AniList “Planning”** (ANIME only, no manga)
* Watchlist writes: **add** and **remove**
* Ratings writes: **add / update** and **remove (clear)**
* Sync model: **present-state snapshot** for watchlists

### Prerequisite: connect AniList

Set up AniList in **Settings → Authentication** first. Use this guide: [AniList (Authentication provider)](/crosswatch/settings/connections/trackers/anilist).

### How matching works (bridging)

Most providers prefer **IMDb/TMDb** IDs. AniList prefers **AniList/MAL** IDs.

CrossWatch tries these steps when it needs an AniList ID:

1. Use an existing `anilist` ID.
2. Else use **Anime ID Mapping** to bridge supported IDs.
3. Else use a `mal` ID and resolve it to AniList.
4. Else do a title search and require a strong match.

{% hint style="info" %}
Best results come from **Anime ID Mapping**.

Enable it in **Settings → Connections → Metadata / ID Mapping**.

Guide: [Anime ID Mapping](/crosswatch/settings/connections/metadata/anime-id-mapping).

For pair-level **Watchlist** and **Ratings** controls, see [AniList pair options](/crosswatch/settings/configure-pairs/anilist-pair-options).
{% endhint %}

### Limitations you should expect

* TMDb anime coverage is incomplete.
* Some titles will map wrong or not at all.
* Unmappable items can cause duplicates across providers.

### Snapshot model (why it behaves “all-or-nothing”)

AniList watchlist is treated as a full snapshot:

* No watermarks.
* No `date_from`.
* Each run re-reads the full Planning list.

### Shadow state (stability across runs)

CrossWatch keeps a local mapping file to avoid re-guessing every run:

`/config/.cw_state/anilist_watchlist_shadow.json`

It is used to:

* reuse stable mappings
* avoid retrying known no-match titles
* keep multi-provider sync paths stable

<details>

<summary>What gets stored in the shadow file</summary>

* AniList IDs, optional MAL IDs
* AniList list entry IDs
* source IDs from the originating provider
* ignored markers and ignore reasons (for “not anime / no match”)

</details>

### Rate limits and token lifecycle

* Run **daily** for normal usage.
* Avoid rapid re-runs while debugging.
* AniList tokens are valid for **1 year**.
* AniList has no refresh token flow in CrossWatch.
* Reconnect yearly in **Authentication** to get a new token.

### Best practices

* Enable **Anime ID Mapping** for AniList pairs.
* Use **separate pairs** for anime vs movies/shows.
* Whitelist libraries to keep non-anime out of the pair.
* Start one-way. Validate matches. Then go bidirectional.


# CrossWatch

Local Tracker adapter notes for syncing watchlist, ratings, history, and progress.

CrossWatch Local Tracker stores and synchronizes tracker data locally. It supports watchlist, ratings, history, and progress.

{% hint style="info" %}
Connect CrossWatch Local Tracker first. Use [CrossWatch Local Tracker (Authentication provider)](/crosswatch/settings/connections/trackers/crosswatch).
{% endhint %}

{% hint style="warning" %}
Local Tracker data and snapshots live in `/config`. Use persistent storage for `/config`.
{% endhint %}

### What it supports

* Direction: source or target in a pair, one-way or two-way
* Features:
  * **Watchlist**
  * **Ratings**
  * **History**
  * **Progress**
  * **Playlists** (not supported)
* Delta sync: no remote delta feed is required. CW reads local state and detects changes during normal pair synchronization.
* IDs: stores identifiers supplied by CW. Watchlist items with an IMDb ID can gain a TMDb ID through metadata resolution.

Each pair keeps separate local tracker state. Additional Local Tracker profiles use separate storage.

{% hint style="info" %}
This adapter reads and writes local files. Provider API rate limits do not apply.
{% endhint %}

### Watchlist behavior

* **Read:** reads the current local watchlist state.
* **Write:** adds or removes movies and shows.
* Stores canonical CW item data and available provider identifiers.
* Resolves a missing TMDb ID from an IMDb ID when metadata resolution is available.

### Ratings behavior

* **Read:** reads locally stored ratings.
* **Write:** upserts or removes ratings.
* Supports movies, shows, seasons, and episodes.
* Retains rating values and timestamps from the synchronization engine.

### History behavior

* **Read:** reads locally stored watched state.
* **Write:** marks items watched or removes watched state.
* Supports movies, shows, seasons, and episodes.
* Retains watch timestamps when available.

### Progress behavior

* **Read:** reads locally stored playback progress.
* **Write:** adds, updates, or removes playback progress.
* Stores positions in milliseconds with available timestamp and duration data.

The **Continue Watching** interface can read local progress. It can update progress, remove it, or mark an item watched.

### Snapshots

CW can create a snapshot before changing local tracker data. Snapshots are stored separately for Watchlist, Ratings, History, and Progress.

* Default retention: `30` days
* Default maximum: `64` snapshots per feature
* Retention: `0` keeps snapshots indefinitely.
* Maximum snapshots: `0` allows unlimited snapshots.
* Restore selection defaults to **Latest** for each feature.

### Settings (advanced)

#### Root directory

Default: `/config/.cw_provider`

Additional profiles use `/config/.cw_provider/profiles/<profile>`.

#### Retention days

Default: `30`

Controls how long snapshots remain available.

#### Auto snapshot

Default: enabled

Creates a snapshot before local tracker data changes.

#### Maximum snapshots

Default: `64` per feature

Controls retained snapshots for watchlist, ratings, history, and progress.

#### Restore snapshot

Default: **Latest**

Select a snapshot separately for each feature.

### Limitations

* Local Tracker is CW storage. It is not a cloud tracker or backup service.
* Removing the CW configuration volume can remove tracker state and snapshots.
* Capacity depends on storage, snapshot retention, and tracker data volume.
* Playlists are not supported.


# Floppy

Floppy adapter notes for syncing watchlist, ratings, history, and progress.

Floppy adapter lets CW synchronize with a Floppy server. It supports watchlist, ratings, history, and progress.

{% hint style="info" %}
Connect Floppy first. Use [Floppy (Authentication provider)](/crosswatch/settings/connections/trackers/floppy).
{% endhint %}

{% hint style="info" %}
CW throttles Floppy requests to reduce server load. Defaults are `20` GET requests per second and `20` POST requests per second. See [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries) to tune them.
{% endhint %}

Floppy is normally self-hosted. It has no fixed synchronization schedule requirement. Very frequent runs can still add unnecessary load.

### What it supports

* Direction: source or target in a pair, one-way or two-way
* Features:
  * **Watchlist** (movies and shows)
  * **Ratings** (movies and shows)
  * **History** (watched movies and episodes)
  * **Progress** (movies and episodes)
  * **Playlists** (not supported)
* Delta sync: not supported. CW reads current Floppy state through paginated API requests.
* IDs: TMDb IDs are required. Episodes also require the show TMDb ID, season number, and episode number.

{% hint style="warning" %}
The Floppy adapter is experimental. This means only use it in one-way pairs
{% endhint %}

### Watchlist behavior

* **Read:** reads movies and shows from the configured Floppy list.
* **Write:** adds or removes movies and shows.
* Default list name: `Watchlist`.
* CW finds the configured list by name.
* If the list is missing, CW creates it when adding an item.
* Items without a TMDb ID cannot synchronize.

The watchlist uses a standard Floppy custom list. It does not enable CW playlist synchronization.

### Ratings behavior

* **Read:** reads ratings from tracked Floppy movies and shows.
* **Write:** upserts or removes ratings.
* Rating scale: `0–10`.
* Values above `10`, through `100`, are converted to the `0–10` scale.
* CW can create the media entry before applying a rating.
* A rating of zero clears the rating.

Season and episode ratings are not supported.

### History behavior

* **Read:** reads completed movies and watched episodes.
* **Write:** marks movies or episodes as watched.
* **Remove:** removes movie or episode watched state.
* Movie history uses the movie TMDb ID.
* Episode history uses the show TMDb ID, season number, and episode number.

Show-level and season-level history are not supported. Movie writes mark the item completed. Episode writes create a watch event when needed.

### Progress behavior

* **Read:** reads movie and episode resume positions.
* **Write:** synchronizes movie and episode resume positions.
* Progress requires a usable movie or episode identity.

Show-level and season-level progress are not supported.

### Pagination

CW reads Floppy results in pages of up to `200` items. Each request sequence has an internal limit of `1000` pages.

This is a CW safety boundary. It is not a confirmed Floppy account or storage limit.

### Settings (advanced)

#### Server URL

Example: `http://floppy:8000`

The server must be reachable from the CW container or host.

#### API token

Create the token in Floppy under **Settings → Advanced**.

#### Verify SSL

Default: disabled

Enable this when Floppy uses HTTPS with a certificate that needs validation.

#### Timeout

Default: `12` seconds

#### Watchlist name

Default: `Watchlist`

Controls the Floppy custom list used for watchlist synchronization.

#### GET rate

Default: `20` requests per second

#### POST rate

Default: `20` requests per second

### Limitations

* **Experimental:** Floppy is new and its API can still change.
* Sync speed depends on where and how you run Floppy. Large imports can take time.
* Large history backfills can be slow. Floppy handles watched records one item at a time.
* Watchlist supports movies and shows only.
* Ratings support movies and shows only. Season and episode ratings are not.
* History supports movies and individual episodes only.
* Progress supports movies and individual episodes only.
* TMDb matching is required. Items without a TMDb ID remain unresolved.


# MDBList

MDBList adapter notes for syncing watchlist, history, ratings, and progress.

MDBList adapter lets CrossWatch sync with MDBList. It supports watchlist, ratings, history, and progress. It prefers external IDs for matching.

{% hint style="warning" %}
Use writes and deletes carefully.

Keep a current MDBList backup or export before larger changes.

If you want a CrossWatch rollback point, create a [Capture](/crosswatch/main-dashboard/navigation/captures) first.

You can also sync MDBList into [Adapter: CrossWatch](/crosswatch/settings/synchronization/others/cw) as a local safety net.
{% endhint %}

### What it supports

* Direction: source or target in a pair (one-way or two-way)
* Features:
  * **Watchlist** (movie/show)
  * **Ratings** (movies, shows, seasons, episodes)
  * **History** (movies, shows, seasons, episodes)
  * **Progress**
  * **Playlists** (not supported)
* Rating scale: **1–10**
* Indexing: present-state snapshot (reads “what exists now”)

{% hint style="info" %}
Connect MDBList first. Use: [MDBList (Authentication provider)](/crosswatch/settings/connections/trackers/mdblist).
{% endhint %}

{% hint style="success" %}
MDBList fixed show-level ratings indexing/upserts (2025-11-11).
{% endhint %}

{% hint style="info" %}
CrossWatch rate-limits MDBList calls to reduce `429 Too Many Requests`.

Defaults match SIMKL (`10` GET/sec, `1` write/sec).

Tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries)
{% endhint %}

### How matching works

CrossWatch keys items by external IDs whenever possible:

* `tmdb`, `imdb`, `tvdb`
* `mdblist` when returned by the API

{% hint style="warning" %}
Watchlist writes only send **TMDb/IMDb**. If an item has only TVDb, CrossWatch can’t write it to MDBList watchlist.
{% endhint %}

### Watchlist behavior

* Reads the current watchlist and normalizes to `{type, ids, title, year}`.
* Writes add/remove operations for movies and shows.

### Ratings behavior

* Reads ratings via a paginated endpoint.
* Normalizes ratings for movies, shows, seasons, and episodes.
* Writes support upsert and “unrate”.

### History behavior

* Reads history for movies, shows, seasons, and episodes.
* Supports incremental history sync from a date watermark.
* Writes support watched upserts and removals.

### Settings (advanced)

The UI shows these in **Synchronization** on the pair that uses MDBList.

<details>

<summary>Endpoints used</summary>

Watchlist:

* `GET /watchlist/items` (supports `limit`, `offset`, `unified=1`)
* `POST /watchlist/items/add`
* `POST /watchlist/items/remove`

Ratings:

* `GET /sync/ratings` (paginated)
* `POST /sync/ratings`
* `POST /sync/ratings/remove`

</details>

<details>

<summary>Important config keys</summary>

Watchlist:

* `watchlist_limit`, `watchlist_offset`

Ratings:

* `ratings_per_page` (default 200)
* `ratings_chunk_size`
* `ratings_write_delay_ms`
* `ratings_max_backoff_ms`

</details>

### Diagnostics

<details>

<summary>Logging and retries</summary>

* Logs are prefixed with `[MDBLIST:watchlist]`, `[MDBLIST:ratings]`, and `[MDBLIST:history]`.
* Requests use retries with exponential backoff.
* Rate-limit headers and `Retry-After` are honored.

</details>

<details>

<summary>State and cache files</summary>

Watchlist:

* Shadow cache: `/config/.cw_state/mdblist_watchlist.shadow.json`
* Unresolved: `/config/.cw_state/mdblist_watchlist.unresolved.json`

Ratings:

* Ratings index cache: `/config/.cw_state/mdblist_ratings.index.json`

History:

* History index cache: `/config/.cw_state/mdblist_history.index.json`

</details>

### Notes and limitations

* Missing external IDs reduces match quality.
* Ratings are de-duplicated by keeping the newest `rated_at` per key.


# PublicMetaDB

PublicMetaDB adapter notes for syncing watchlist, history, ratings, and progress with TMDb-based matching.

PublicMetaDB adapter lets CrossWatch sync with PublicMetaDB. It supports watchlist, history, ratings, and progress.

{% hint style="info" %}
Connect PublicMetaDB first. Use: [PublicMetaDB (Authentication provider)](/crosswatch/settings/connections/trackers/publicmetadb).
{% endhint %}

{% hint style="info" %}
PublicMetaDB uses request pacing, retry/backoff, and hourly rating quotas.

Defaults and tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries).
{% endhint %}

### What it supports

* Direction: source or target in a pair (one-way or two-way)
* Features:
  * **Watchlist** (movies and shows)
  * **History** (movies and episodes)
  * **Ratings** (movies, shows, and episodes)
  * **Progress** (movies and episodes)
  * **Playlists** (not supported)
* Rating scale: CrossWatch **1–10**, converted to PublicMetaDB **0–100**
* Indexing: present-state snapshot (reads “what exists now”)

### How matching works

PublicMetaDB works best with TMDb IDs.

CrossWatch uses TMDb IDs to tell PublicMetaDB exactly which item changed.

If you want title and year enrichments, configure [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata).

For writes, CrossWatch usually needs:

* Movies: `ids.tmdb`
* Episodes: show `ids.tmdb` plus `season` and `episode`
* History: `watched_at`
* Progress: current position plus total duration

{% hint style="warning" %}
If an item has no TMDb ID, PublicMetaDB sync often can’t write it.

If an episode is missing show TMDb ID or episode numbers, CrossWatch marks it unresolved.
{% endhint %}

### TMDb metadata enrichment

CrossWatch can fill missing titles and release years for PublicMetaDB items.

This enrichment applies to:

* Watchlist
* History
* Ratings
* Progress

It runs only when:

* the PublicMetaDB item already has a TMDb ID
* the item is missing a title or release year
* [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata) is configured with a valid API key

CrossWatch preserves existing metadata.

If a title or year is already present, CrossWatch keeps it.

For episodes, CrossWatch enriches the entry from the parent show.

Enrichment uses the configured TMDb metadata locale and cache settings.

### Using PublicMetaDB in a pair

1. Open **Settings → Authentication** and connect PublicMetaDB.
2. Create or edit a pair.
3. Choose **PublicMetaDB** as the source, destination, or both.
4. Enable **Watchlist**, **History**, **Ratings**, or **Progress**.
5. Optionally set a pair-level watchlist name.
6. Save and run the pair.

Internal provider name: `PUBLICMETADB`.

### Watchlist behavior

* Supports movies and shows.
* Supports source, destination, or two-way sync.
* Uses a PublicMetaDB list as the destination container.
* Default list name: `Watchlist`.
* If the list does not exist, CrossWatch can create it automatically.

Created lists are private and marked as managed by CrossWatch.

Pair-level overrides can use a different list name for each pair.

Examples:

* `Movies to Watch`
* `Family Watchlist`
* `Anime Queue`

{% hint style="warning" %}
PublicMetaDB watchlist does not support seasons or individual episodes.
{% endhint %}

### History behavior

* Supports movies and episodes.
* Reads watched items from PublicMetaDB and can sync them elsewhere.
* Writes watched movies and watched episodes into PublicMetaDB.

History is event-based.

The same movie or episode can appear more than once if it was watched multiple times.

CrossWatch keeps those events distinct by including `watched_at` in the internal key.

### Ratings behavior

* Supports movies, shows, and episodes.
* Uses a CrossWatch `1–10` rating scale.
* Converts ratings before sending them to PublicMetaDB.

Examples:

* `8/10` → `80`
* `10/10` → `100`

Default label: `Overall`.

Power users can change the default with `publicmetadb.ratings_label`.

Ratings are label-aware.

A rating for the same item with a different label is treated as a separate rating.

### Progress behavior

* Supports movies and episodes.
* Syncs partial playback state such as resume position.
* Requires current position and total duration.

PublicMetaDB needs both values to store progress.

If duration is missing, CrossWatch can’t send the progress entry.

If PublicMetaDB reports an item as completed or ignored after a progress update, CrossWatch removes the local resume reference from shadow state.

Related: [Progress](/crosswatch/settings/configure-pairs/features/progress).

### Settings and state (advanced)

<details>

<summary>Important config keys</summary>

Stored under `publicmetadb`.

Core settings:

* `api_key`
* `base_url`
* `timeout`
* `max_retries`

Watchlist:

* `watchlist_list_id`
* `watchlist_name`
* `watchlist_auto_create`
* `watchlist_page_size`

History and progress:

* `history_per_page`
* `history_max_pages`
* `progress_per_page`
* `progress_max_pages`

Ratings and pacing:

* `ratings_label`
* `ratings_submit_per_hour`
* `ratings_update_per_hour`
* `rate_limit.get_per_sec`
* `rate_limit.post_per_sec`

Defaults:

* `base_url`: `https://publicmetadb.com`
* `timeout`: `15.0`
* `max_retries`: `3`
* `ratings_label`: `Overall`
* `rate_limit.get_per_sec`: `20`
* `rate_limit.post_per_sec`: `3`
* `ratings_submit_per_hour`: `200`
* `ratings_update_per_hour`: `100`

</details>

<details>

<summary>Rate control layers</summary>

PublicMetaDB uses three layers:

* Request pacing for `GET` and non-`GET` calls
* Retry and backoff for `429` and temporary `5xx` errors
* Pair-scoped hourly rating quotas

`DELETE` shares the same pacing bucket as `POST`.

For `429`, CrossWatch honors `Retry-After` when PublicMetaDB sends it.

If a rating quota is reached, CrossWatch marks affected items unresolved and finishes the run.

</details>

<details>

<summary>Watchlist selection order</summary>

CrossWatch chooses the destination list in this order:

1. Use `publicmetadb.watchlist_list_id` if set.
2. Find a list matching `watchlist_name`.
3. Use the first list with type `watchlist`.
4. Fall back to common names like `crosswatch` or `watchlist`.
5. Create a private list if auto-create is enabled.

</details>

<details>

<summary>Local shadow and quota files</summary>

Stored under `/config/.cw_state/`.

Typical files:

* `publicmetadb_watchlist.<pair-scope>.shadow.json`
* `publicmetadb_history.<pair-scope>.shadow.json`
* `publicmetadb_progress.<pair-scope>.shadow.json`
* `publicmetadb_ratings.<pair-scope>.shadow.json`
* `publicmetadb_ratings.<pair-scope>.quota.json`

If no pair scope is available, CrossWatch uses `unscoped`.

Deletes usually need PublicMetaDB remote item IDs, not only TMDb IDs.

That makes the first sync or build-index pass important.

</details>

<details>

<summary>Common unresolved reasons</summary>

* `missing_tmdb_id` — item has no TMDb ID
* `missing_show_tmdb_or_episode_numbers` — episode is missing show TMDb ID, season, or episode
* `missing_watched_at` — history item has no watched timestamp
* `missing_duration` — progress item has no runtime or duration
* `missing_remote_item_id` — missing remote ID for watchlist remove
* `missing_remote_history_id` — missing remote ID for history remove
* `missing_remote_resume_id` — missing remote ID for progress remove
* `missing_remote_rating_id` — missing remote ID for rating remove
* `publicmetadb_hourly_rating_submit_limit` — local rating submit quota reached
* `publicmetadb_hourly_rating_update_limit` — local rating update quota reached

</details>

### Notes and limitations

* Watchlist sync does not support seasons or episodes.
* History sync does not support whole-show watched state.
* Progress sync does not support whole-show progress.
* Ratings sync depends on local shadow state for stable remove behavior.
* Reliable TMDb IDs are the main compatibility requirement.
* TMDb metadata enrichment only fills missing fields. It does not overwrite existing titles or years.

Pair compatibility still depends on what the other provider supports.

Good pair examples:

* Plex → PublicMetaDB history
* PublicMetaDB → Plex watchlist
* Jellyfin → PublicMetaDB progress


# PunchPlay

PunchPlay adapter notes for syncing watchlist, ratings, history, and progress.

PunchPlay can synchronize with CrossWatch as a source or target. It supports one-way and two-way pairs.

{% hint style="warning" %}
**PunchPlay support is experimental.** The adapter is new and may change as it is tested and improved.
{% endhint %}

### Supported features

| Feature   | Supported media                  | Read | Write |
| --------- | -------------------------------- | ---- | ----- |
| Watchlist | Movies, shows                    | Yes  | Yes   |
| Ratings   | Movies, shows, seasons, episodes | Yes  | Yes   |
| History   | Movies, episodes                 | Yes  | Yes   |
| Progress  | Movies, episodes                 | Yes  | Yes   |

PunchPlay does not support playlist synchronization.

### Using PunchPlay in a pair

First, connect PunchPlay under **Settings** → **Authentication**.

You can then select PunchPlay when creating or editing a synchronization pair.

Examples:

`Plex → PunchPlay`

`PunchPlay → Trakt`

`SIMKL → PunchPlay`

`PunchPlay ↔ CrossWatch Tracker`

CrossWatch only shows features supported by both providers.

Start a new pair with **one-way synchronization** and one feature. Enable more features or two-way synchronization after confirming the results.

### Watchlist

PunchPlay Watchlist synchronization supports:

`Movies`

`Shows`

CrossWatch can add and remove items from the PunchPlay watchlist.

Seasons and episodes are not supported as Watchlist items.

### Ratings

PunchPlay Ratings synchronization supports:

`Movies`

`Shows`

`Seasons`

`Episodes`

PunchPlay uses ratings from **1 to 10**.

Ratings written to PunchPlay are stored as whole numbers. CrossWatch rounds ratings when required.

Ratings can also be removed when removal is enabled for the pair.

### History

PunchPlay History synchronization supports:

`Movies`

`Episodes`

CrossWatch preserves the watched timestamp when writing history to PunchPlay.

Shows and seasons are not written as standalone History entries.

History removals are supported when removal is enabled for the pair.

### Progress

PunchPlay supports Playback Progress synchronization for:

`Movies`

`Episodes`

This synchronizes unfinished playback positions with another provider that supports Progress.

Examples:

`Plex ↔ PunchPlay`

`Jellyfin ↔ PunchPlay`

`Emby ↔ PunchPlay`

Progress is separate from History. An unfinished position does not automatically mark an item watched.

### Matching and IDs

CrossWatch uses provider IDs whenever possible when synchronizing with PunchPlay.

PunchPlay supports these IDs for most operations:

`TMDb`

`IMDb`

`TVDb`

`MAL`

TMDb is the primary ID PunchPlay returns to CrossWatch.

Playback Progress uses TMDb, IMDb, or TVDb IDs.

For the best matching results, configure **TMDb Metadata** in CrossWatch.

### Deletes

PunchPlay detects and applies removals for Watchlist, Ratings, History, and Progress.

CrossWatch safety controls still apply.

CrossWatch only removes data when the feature and pair configuration allow it.

For a new PunchPlay pair, leave removals disabled until after a clean synchronization run.

### Profiles

PunchPlay supports CrossWatch Profiles.

When multiple PunchPlay accounts are connected, select the correct profile for each pair side.

Each profile is handled independently.

### Rate limits

CrossWatch automatically handles PunchPlay API pacing, batching, and retries.

Writes use batches where supported. CrossWatch slows requests when PunchPlay reports API limits.

Most users do not need to change PunchPlay rate-limit settings.

### Experimental status

The PunchPlay sync adapter is version `0.1` and remains experimental.

When reporting a reproducible sync issue, include the feature, pair direction, and relevant Events output.


# Scrob

Scrob adapter notes for syncing watchlist, ratings, history, and progress.

Scrob can synchronize with CrossWatch as a source or target. It supports one-way and two-way pairs.

{% hint style="warning" %}
**Scrob support is experimental.** The adapter is new and may change as it is tested and improved.
{% endhint %}

### Supported features

| Feature   | Supported media                  | Read | Write |
| --------- | -------------------------------- | ---- | ----- |
| Watchlist | Movies, shows                    | Yes  | Yes   |
| Ratings   | Movies, shows, seasons, episodes | Yes  | Yes   |
| History   | Movies, episodes                 | Yes  | Yes   |
| Progress  | Movies, episodes                 | Yes  | Yes   |

Scrob does not support playlist synchronization.

### Using Scrob in a pair

First, connect Scrob under **Settings** → **Connections** → **Trackers** → **Scrob**.

You can then select Scrob when creating or editing a synchronization pair.

Examples:

`Trakt → Scrob`

`Scrob → Trakt`

`Trakt ↔ Scrob`

CrossWatch only shows features supported by both providers.

Start a new pair with **one-way synchronization** and one feature. Enable more features or two-way synchronization after confirming the results.

Only enable **Remove** when source deletions should affect Scrob.

### Watchlist

Scrob Watchlist synchronization supports:

`Movies`

`Shows`

CrossWatch maps the Scrob watchlist to a personal list named `Watchlist`.

If CrossWatch needs to write to a missing list, it creates a private list. Items can be added or removed in either direction.

Seasons and episodes are not supported as Watchlist items.

### Ratings

Scrob Ratings synchronization supports:

`Movies`

`Shows`

`Seasons`

`Episodes`

Scrob uses ratings from **1 to 10**. CrossWatch rounds incoming ratings to the nearest supported whole number.

CrossWatch can create, update, and remove ratings. Episode ratings require the episode to already exist in Scrob.

### History

Scrob History synchronization supports:

`Movies`

`Episodes`

Scrob stores individual play events rather than one watched state. CrossWatch preserves timestamps, multiple plays, and rewatches where the other provider supports them.

CrossWatch can add and remove individual history entries when removal is enabled for the pair.

Shows and seasons are not written as standalone History entries.

### Progress

Scrob supports Playback Progress synchronization for:

`Movies`

`Episodes`

Progress is a native CrossWatch sync feature. It is separate from Watcher and real-time scrobbling.

CrossWatch can read, add, update, and remove stored progress. Scrob exposes this data through its Continue Watching state.

For example, `Trakt → Scrob` with **Progress** enabled synchronizes supported playback positions to Scrob.

#### Progress range

Scrob keeps active Continue Watching positions above `5%` and below `90%`.

CrossWatch only writes progress inside this range. It also requires a known playback duration.

Progress at or below `5%`, or at or above `90%`, is not stored as an active position.

### Matching and IDs

CrossWatch primarily uses TMDb identifiers when synchronizing with Scrob.

Movies and shows normally require a TMDb ID. Episode operations also require the show TMDb ID, season number, and episode number.

TVDb information can support some episode operations when available. Good metadata matching is important when the other provider uses different identifiers.

### Deletes

Scrob detects and applies removals for Watchlist, Ratings, History, and Progress.

CrossWatch safety controls still apply. CrossWatch only removes data when the feature and pair configuration allow it.

For a new Scrob pair, leave removals disabled until after a clean synchronization run.

### Power users

#### Adapter

The Scrob synchronization provider is implemented in:

`providers/sync/_mod_SCROB.py`

Feature implementations are located in:

`providers/sync/scrob/_watchlist.py`

`providers/sync/scrob/_ratings.py`

`providers/sync/scrob/_history.py`

`providers/sync/scrob/_progress.py`

The sync adapter is version `0.1` and remains experimental.

#### Provider capabilities

```
bidirectional: true
provides_ids: true
index_semantics: present
can_source: true
can_target: true

watchlist: true
ratings: true
history: true
progress: true
playlists: false
```

#### Watchlist semantics

Scrob supports Watchlist reads, additions, removals, and observed deletes for movies and shows.

TMDb is required. The default list name is `Watchlist`.

Set the Scrob provider option `watchlist_name` to use a different list. CrossWatch creates the configured list when its first write requires it.

#### Ratings semantics

Scrob supports reads, additions, updates, removals, and observed deletes for movies, shows, seasons, and episodes.

The rating scale has a minimum of `1`, maximum of `10`, and step of `1`. TMDb is the primary identifier.

Episode ratings require Scrob to already know the episode.

#### History semantics

Scrob History uses event semantics. Separate plays remain separate events instead of collapsing into one watched state.

```
event_history: true
rewatches_read: true
rewatches_write: true
```

CrossWatch preserves individual timestamps when available. Movies use TMDb as the primary identifier.

Episodes use a series TMDb ID, season number, and episode number. A TVDb series ID can also be included.

#### Progress semantics

Scrob supports reads, additions, updates, removals, and observed deletes for movie and episode progress.

Writes require a supported media identifier, playback percentage, and playback duration.

```
minimum: greater than 5%
maximum: less than 90%
```

CrossWatch converts Scrob Continue Watching data into the common Progress model. Movie progress can use TMDb or IMDb data.

Episode progress uses show identifiers with season and episode numbers.

#### Progress removal

CrossWatch stores the Scrob media identifier with each indexed Continue Watching entry.

It uses that identifier to remove the corresponding entry later.

#### Observed deletes

Scrob supports observed deletes for Watchlist, Ratings, History, and Progress.

Normal deletion safeguards still apply. The pair feature must allow removal before CrossWatch writes a deletion to Scrob.

#### Index behavior

The Scrob adapter uses `index_semantics: present`.

CrossWatch reads each enabled feature into the provider index. It then compares both sides and plans additions, updates, or removals.

#### Health check

The adapter checks the Scrob Now Playing API.

A successful response marks Scrob as available for synchronization.

#### API and authentication

Synchronization uses the Scrob connection configured under **Settings** → **Connections** → **Trackers** → **Scrob**.

The adapter uses the stored Scrob API key and authenticated account session. Pairs do not need additional credentials.

### Summary

Scrob is a bidirectional CrossWatch synchronization provider for Watchlist, Ratings, History, and Progress.

It can operate on either side of a standard synchronization pair. Progress is a native sync feature and does not depend on Watcher or real-time scrobbling.


# SIMKL

SIMKL adapter notes for syncing watchlist, history, ratings, and progress.

SIMKL adapter lets CrossWatch sync with SIMKL. It supports watchlist, ratings, history, and progress, including anime.

{% hint style="info" %}
Connect SIMKL first. Use: [SIMKL (Authentication provider)](/crosswatch/settings/connections/trackers/simkl).
{% endhint %}

{% hint style="info" %}
CrossWatch rate-limits SIMKL calls to reduce `429 Too Many Requests`.

Default: `10` GET/sec, `1` write/sec.

Tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries)
{% endhint %}

{% hint style="warning" %}
Be kind to SIMKL’s API. Don’t schedule SIMKL runs more often than **every 12 hours**. Daily is better.
{% endhint %}

### What it supports

* Direction: source or target in a pair (one-way or two-way)
* Features:
  * **Watchlist** → SIMKL **PTW** (“Plan To Watch”)
  * **Ratings** (1–10)
  * **History** (watched movies + watched episodes)
  * **Progress**
  * **Playlists** (not supported)
* Delta sync: uses SIMKL **activities** and **watermarks** to avoid full downloads
* IDs: works best with `simkl`, `imdb`, `tmdb`, `tvdb`
  * Anime can also use `mal`, `anilist`, `kitsu`, `anidb` when available

{% hint style="warning" %}
SIMKL PTW is a **status list**, not a “save for later” watchlist. Items can leave PTW due to SIMKL status changes.
{% endhint %}

### Watchlist behavior (PTW)

* Read: incremental list reads per bucket (`movies`, `shows`, `anime`)
* Write: add/remove items to PTW

{% hint style="info" %}
SIMKL writes mainly use movies/shows payloads. CrossWatch still reads the anime bucket for indexing.
{% endhint %}

### Ratings behavior

* Read: incremental ratings feed (movies/shows/anime)
* Write: upsert and remove ratings

{% hint style="info" %}
Writing anime ratings depends on how the source provider represents anime.
{% endhint %}

### History behavior

* Read: incremental watched state for movies/shows/anime
* Write: mark watched or unwatch

Anime writes can add `use_tvdb_anime_seasons=true` to help season mapping.

### Settings (advanced)

<details>

<summary>Headers sent to SIMKL</summary>

* `simkl-api-key: <client id>`
* `Authorization: Bearer <access token>`
* `User-Agent: CrossWatch/3.x (SIMKL)` (override with `CW_UA`)
* `Accept: application/json`
* `Content-Type: application/json`

</details>

<details>

<summary>Delta syncing: date_from and watermarks</summary>

SIMKL uses per-feature watermarks stored under:

* `/config/.cw_state/simkl.watermarks.&lt;pair&gt;.json`

For each feature, CrossWatch picks `date_from` in this order:

1. Stored watermark
2. `SIMKL_<FEATURE>_DATE_FROM`
3. `SIMKL_DATE_FROM`
4. `simkl.date_from`
5. `1970-01-01T00:00:00Z`

Watermarks only move forward.

</details>

<details>

<summary>Health check</summary>

SIMKL health is probed with:

* `POST /sync/activities`

It also acts as an “activities gate” to skip work when nothing changed.

</details>

<details>

<summary>State files (per pair)</summary>

All state lives in `/config/.cw_state/`:

* `simkl.watermarks.<pair>.json`
* `simkl.watchlist.shadow.<pair>.json`
* `simkl.watchlist.unresolved.<pair>.json`
* `simkl.ratings.shadow.<pair>.json`
* `simkl_ratings.unresolved.<pair>.json`
* `simkl.history.shadow.<pair>.json`
* `simkl_history.unresolved.<pair>.json`
* `simkl.show.map.<pair>.json`

Deleting these forces rebuilds and usually increases API calls next run.

</details>


# TMDb

TMDb adapter notes for syncing TMDb account data (watchlist/ratings) via TMDb (Sync).

TMDb adapter lets CrossWatch sync with your TMDb account. It supports watchlist and ratings.

{% hint style="info" %}
Connect TMDb first. Use: [TMDb](/crosswatch/settings/connections/trackers/tmdb).
{% endhint %}

### What it supports

* Direction: source or target in a pair (one-way or two-way)
* Features:
  * **Watchlist** (movies and shows)
  * **Ratings** (movies, shows, and episodes)
  * **History / Playlists** (not supported)
* Rating scale: **1–10**
* Indexing: present-state snapshot (reads “what exists now”)

### How matching works

TMDb writes need a TMDb media ID.

CrossWatch resolves IDs in this order:

1. `ids.tmdb` (best)
2. `ids.imdb` via TMDb `/find`
3. `ids.tvdb` via TMDb `/find` (TV only)

CrossWatch UI features like **Insights/Overview** depend on `ids.imdb`.

So the TMDb adapter will also do an external lookup when it has a TMDb ID.

It calls TMDb `external_ids` and attaches `ids.imdb` when available.

{% hint style="warning" %}
If an item has no `tmdb`, `imdb`, or `tvdb` ID, it can’t be written to TMDb.
{% endhint %}

### Watchlist behavior

* Read: your TMDb watchlist for movies and TV.
* Write: add/remove items from the watchlist.

CrossWatch does not sync TMDb custom lists.

### Ratings behavior

* Read: your rated movies and TV shows.
* Write: set or clear ratings.

Ratings are integers from **1** to **10**.

{% hint style="info" %}
TMDb has API support for episode ratings. CrossWatch may index them. Some UIs hide episode rating writes based on feature gates.
{% endhint %}

### Settings (advanced)

TMDb (Sync) stores credentials under `tmdb_sync`.

<details>

<summary>Important config keys</summary>

* `tmdb_sync.api_key` (TMDb v3 API key)
* `tmdb_sync.session_id` (session from the connect flow)
* `tmdb_sync.account_id` (optional, auto-discovered if missing)
* `tmdb_sync.timeout` (seconds)
* `tmdb_sync.max_retries`

</details>

<details>

<summary>Endpoints used (for debugging)</summary>

Watchlist:

* `GET /account/{account_id}/watchlist/movies`
* `GET /account/{account_id}/watchlist/tv`
* `POST /account/{account_id}/watchlist`

Ratings:

* `GET /account/{account_id}/rated/movies`
* `GET /account/{account_id}/rated/tv`
* `GET /account/{account_id}/rated/tv/episodes` (episode ratings)
* `POST /movie/{id}/rating` and `DELETE /movie/{id}/rating`
* `POST /tv/{id}/rating` and `DELETE /tv/{id}/rating`
* `POST /tv/{show}/season/{s}/episode/{e}/rating` and `DELETE .../rating`

ID resolution:

* `GET /find/{external_id}` (`imdb_id` / `tvdb_id`)

ID enrichment (adds `ids.imdb` / `ids.tvdb` when TMDb provides them):

* `GET /{media_type}/{tmdb_id}/external_ids`

</details>

### Diagnostics

* Logs include `TMDB` and the feature name (example: watchlist, ratings).
* Health check probes `GET /account`.

Common unresolved reasons:

* Missing IDs for writes.
* Invalid rating (must be `1–10`).
* Episode rating missing `season` or `episode`.

### State and caching (advanced)

TMDb sync uses local state files under `/config/.cw_state/`.

These files are typically scoped per pair run, so different pairs don’t collide.

Scope details: [Scope](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/AW0YhA9Rjjc0lx6UjKbg).

### Notes and limitations

* TMDb has no history sync. It is watchlist + ratings only.
* For better matching, enable [TMDb Metadata](/crosswatch/settings/connections/metadata/tmdb-metadata).
* Start one-way + dry run. Add removals only once runs are clean.

Deep dive (module-level): [TMDb (Sync) module overview](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/4a585675c6399e298c8e10ee03c85869b2c270eb).


# Trakt

Trakt adapter notes for syncing watchlist, history, ratings, and progress.

Trakt adapter lets CrossWatch sync with Trakt. It supports watchlist, ratings, history, and progress.

{% hint style="info" %}
Connect Trakt first. Use: [Trakt (Authentication provider)](/crosswatch/settings/connections/trackers/trakt).
{% endhint %}

{% hint style="info" %}
CrossWatch rate-limits Trakt calls to reduce `429 Too Many Requests`.

Default: `3.33` GET/sec, `1` write/sec.

Tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries)
{% endhint %}

### What it supports

* Direction: source or target in a pair (one-way or two-way)
* Features:
  * **Watchlist**
  * **Ratings** (1–10)
  * **History** (plays with `watched_at`)
  * **Progress**
  * **Playlists** (not supported)
* Indexing: present-state snapshot (reads “what exists now”)
* IDs: `trakt`, `imdb`, `tmdb`, `tvdb`, and sometimes `slug`

### Watchlist behavior

* Read: `/sync/watchlist`
* Write: add/remove watchlist entries

### Ratings behavior

* Read: ratings for movies/shows/seasons/episodes
* Write: upsert and remove ratings

{% hint style="warning" %}
Trakt ratings are **integers 1–10**. Anything else is rejected.
{% endhint %}

### History behavior

* Read: movie and episode play history
* Write:
  * Add = scrobble with `watched_at`
  * Remove = remove plays (“unscrobble”)

Multiple plays are supported by sending multiple distinct `watched_at` timestamps.

{% hint style="info" %}
Trakt is currently the only provider with full multiple-play support.

CrossWatch does not preserve full multi-play history across providers.

Repeated plays can collapse to one history entry on providers without matching support.
{% endhint %}

### History options (advanced)

<details>

<summary>Options under Pair → History → Advanced</summary>

**Add collections to Trakt**

* When syncing history **to Trakt**, also writes collection membership.
* You can enable this separately for **Movies** and **Shows**.
* Default: **Movies** enabled.
* This can increase write volume on large libraries.

**Number Fallback**

* If episode IDs are missing but you have show IDs + season/episode numbers, CrossWatch posts using the `shows → seasons → episodes` payload.

</details>

### Settings and state (advanced)

<details>

<summary>Endpoints used</summary>

Watchlist:

* `GET /sync/watchlist`
* `POST /sync/watchlist`
* `POST /sync/watchlist/remove`

Ratings:

* `GET /sync/ratings/{movies|shows|seasons|episodes}`
* `POST /sync/ratings`
* `POST /sync/ratings/remove`

History:

* `GET /sync/history/{movies|episodes}`
* `POST /sync/history`
* `POST /sync/history/remove`

</details>

<details>

<summary>Local cache and unresolved files</summary>

Stored under `/config/.cw_state/`.

* Watchlist ETag/shadow: `trakt_watchlist.shadow.json`
* Watchlist unresolved: `trakt_watchlist.unresolved.json`
* Ratings index cache: `trakt_ratings.index.json`
* History unresolved: `trakt_history.unresolved.json`

</details>

### Diagnostics

* Logs are prefixed with `[TRAKT:watchlist]`, `[TRAKT:ratings]`, `[TRAKT:history]`.
* Requests use retries with exponential backoff.
* Trakt rate-limit headers (`X-RateLimit-*`) are honored.
* OAuth tokens are refreshed automatically when needed.


# Others

Adapter reference for CW and Tautulli.

Adapter reference for CW and Tautulli.


# CW

Local backup adapter that stores watchlist/history/ratings on disk for rollback and restore.

CW adapter is a **local backup provider**. It stores watchlist, history, and ratings as JSON in your config folder.

Use it as a safety net:

* Backup: `Plex/Emby/Jellyfin/Trakt/SIMKL/MDBList/TMDb → CW`
* Restore: `CW → any provider`

{% hint style="info" %}
Nothing runs “automatically”. CW adapter only does work when it’s used in a pair.
{% endhint %}

### What it supports

* Features: **Watchlist**, **History**, **Ratings**
* Auth: none (local only)
* Storage:
  * Current state: `/config/.cw_provider/`
  * Snapshots: `/config/.cw_provider/snapshots/`
* Restore granularity: per feature (pick different snapshots per feature)

### Storage layout

Current state files:

* `/config/.cw_provider/watchlist.json`
* `/config/.cw_provider/history.json`
* `/config/.cw_provider/ratings.json`

Snapshots are timestamped:

* `YYYYMMDDThhmmssZ-watchlist.json`
* `YYYYMMDDThhmmssZ-history.json`
* `YYYYMMDDThhmmssZ-ratings.json`

### When snapshots are created

Snapshots are only created when CW needs to **change** its local data.

Typical case:

* CW is the **target** in a run.
* The run applies add/remove operations.
* If **Auto snapshot** is enabled, CW snapshots the old state first.

### Settings

Open **Settings** → **Connections** → **Authentication** → **CW Local Tracker**.

Use the connection’s **Settings** tab to manage local tracker behavior.

#### Basic behavior

* **Connection status**: connect the local tracker before using it in pairs.
* **Retention (days)**: `0` keeps snapshots forever.
* **Auto snapshot**: snapshots before writes.
* **Max snapshots per feature**: `0` = unlimited.

#### Restore snapshots (per feature)

Pick one snapshot for each feature:

* Watchlist snapshot
* History snapshot
* Ratings snapshot

`Latest (default)` uses the most recent snapshot for that feature.

<details>

<summary>What gets written to config</summary>

```jsonc
"crosswatch": {
  "enabled": true,
  "retention_days": 30,
  "auto_snapshot": true,
  "max_snapshots": 64,
  "restore_watchlist": "latest",
  "restore_history": "latest",
  "restore_ratings": "latest"
}
```

</details>

### What “restore” means

Selecting a snapshot does **not** push changes to other providers.

It only changes what CW exposes as its **current state**:

* The chosen snapshot is copied into the main JSON for that feature.
* A restore happens only when you run a pair where CW is the **source**.

{% hint style="warning" %}
Snapshot selection “arms” the restore. Running a pair “applies” the restore.
{% endhint %}

### Common workflows

{% stepper %}
{% step %}

### Undo a bad sync (restore ratings)

1. Open the **CW Local Tracker** connection’s **Settings** tab.
2. Set **Ratings snapshot** to a time before the bad run.
3. **Save**.
4. Run a pair: **CW → target provider**, enable **Ratings** only.
   {% endstep %}

{% step %}

### Roll back watchlist only

1. Set **Watchlist snapshot** to an older file.
2. Keep History/Ratings on **Latest**.
3. Run a pair: **CW → target provider**, enable **Watchlist** only.
   {% endstep %}

{% step %}

### Migrate state to a new provider

1. Pick snapshots you trust for the features you want.
2. Run a pair: **CW → new provider**.
3. Start one-way first. Use one feature. Then expand.
   {% endstep %}
   {% endstepper %}

### Tips and limitations

* CW is **local**. Remove `/config/.cw_provider/` and you lose backups.
* Low retention + frequent runs will prune older snapshots quickly.
* CW history does **not** preserve multiple watch entries for one item.
* Trakt is currently the only provider with full multiple-play support.
* This is intentional. Provider-neutral history matters more than Trakt-specific workarounds.
* This is extra protection. It does not replace provider-side backups (if any).

Recommended baseline:

* Enabled: **On**
* Auto snapshot: **On**
* Retention: **30–90** days
* Max snapshots per feature: **64**
* Restore selection: **Latest**, until you need a rollback


# Tautulli

Tautulli adapter notes for importing Plex watch history into CW.

Tautulli adapter is a **read-only history importer**. It pulls old Plex plays from an existing Tautulli database and normalizes them.

{% hint style="warning" %}
Tautulli is not a tracker and not a media server. Don’t use it to “sync” back into Plex/Trakt/SIMKL/etc.
{% endhint %}

{% hint style="info" %}
Connect Tautulli first. Use: [Tautulli (Authentication provider)](/crosswatch/settings/connections/others/tautulli).
{% endhint %}

### What it supports

* Direction: **source-only**
* Features:
  * **History** (movies + episodes)
  * Watchlist / ratings / playlists (not supported)
* Writes: **not supported** (always read-only)

### How indexing works

1. Pull pages from `get_history` until the end or a safety stop.
2. Keep only `media_type` in `{movie, episode}`.
3. Extract IDs from GUIDs + Plex/Tautulli keys (`rating_key`, `grandparent_rating_key`).
4. If IDs are incomplete, call `get_metadata(rating_key)` to enrich external IDs.
5. Emit normalized items keyed by external IDs where possible.

{% hint style="info" %}
History is indexed as a de-duplicated snapshot. Newest entry wins per canonical key.
{% endhint %}

### Output shape

<details>

<summary>Movie example</summary>

```json
{
  "type": "movie",
  "ids": {
    "imdb": "tt123",
    "tmdb": "456",
    "tvdb": "0",
    "plex": "12345",
    "guid": "plex://movie/..."
  },
  "title": "…",
  "year": 2024,
  "watched_at": "2025-12-30T23:54:11Z"
}
```

</details>

<details>

<summary>Episode example</summary>

```json
{
  "type": "episode",
  "ids": {
    "imdb": "ttSHOW",
    "tmdb": "157741",
    "tvdb": "397424",
    "plex": "40759",
    "guid": "plex://show/..."
  },
  "title": "Episode title",
  "year": 2025,
  "season": 2,
  "episode": 4,
  "watched_at": "2025-12-30T23:54:11Z",
  "series_title": "Show title (when available)",
  "show_ids": {
    "imdb": "ttSHOW",
    "tmdb": "157741",
    "tvdb": "397424",
    "plex": "40759",
    "guid": "plex://show/..."
  }
}
```

</details>

### Workflows (recommended)

{% stepper %}
{% step %}

### Import into CW (recommended)

Run a pair: **Tautulli → CW** with **History** enabled.

Then use **CW** as the source to seed Trakt/SIMKL/etc.

Related: [Adapter: CW](/crosswatch/settings/synchronization/others/cw).
{% endstep %}

{% step %}

### Import into Editor dataset

Use this if you want to clean history before syncing it anywhere:

* fix wrong matches
* remove junk plays
* merge duplicates
* correct titles/years
  {% endstep %}
  {% endstepper %}

### Endpoints used (for debugging)

<details>

<summary>API calls</summary>

* History pages: `GET /api/v2?cmd=get_history`
* Metadata enrichment (optional): `GET /api/v2?cmd=get_metadata&rating_key=...`

</details>


# Configure Pairs

How to configure a sync pair, feature toggles, and safety controls.

Pairs define what syncs, in which direction, and with what safety rules.

* Left = **Source**
* Right = **Target**

Enable only what you need. In one-way pairs, the Source defines desired state.

Two-way pairs use feature-specific conflict rules. The Source is the final fallback.

{% hint style="info" %}
First run: **one-way** and **one feature**
{% endhint %}

{% hint style="success" %}
Most safe first pairs are media server → tracker.
{% endhint %}

{% hint style="info" %}
Defaults are good for most users.

Most setups only require:

* Pick Source and Target
* Pick the right **profiles**
* Enable **Watchlist**, **Ratings**, **History**, and/or **Progress**
  {% endhint %}

### Header (the basics)

* **Enabled**: master switch for the pair.
* **Source / Target**: provider pickers.
* **Source profile / Target profile**: pick which profile to use.
  * Defaults to `default`.
  * See [Profiles](/crosswatch/settings/connections/profiles).
* **Mode**:
  * **One-way**: target follows source.
  * **Two-way**: both sides contribute.

{% hint style="warning" %}
Choose **Two-way** only when you already trust matching on both sides.

For most setups, **One-way** is safer.
{% endhint %}

<figure><img src="/files/5fwDpevnxe6Szkk0EDUi" alt=""><figcaption></figcaption></figure>

### Feature tabs

Feature tabs show up only when **both** providers support that feature.

Start here for feature-specific options: [Features](/crosswatch/settings/configure-pairs/features).

Pairs that contain **AniList** can show extra options on **Watchlist** and **Ratings**.

That includes **Use Anime ID Mapping** and, when AniList can receive writes, **Anime-only sync**.

See [AniList pair options](/crosswatch/settings/configure-pairs/anilist-pair-options).

* [Watchlist](/crosswatch/settings/configure-pairs/features/watchlist)
* [Ratings](/crosswatch/settings/configure-pairs/features/ratings)
* [History](/crosswatch/settings/configure-pairs/features/history)
* [Progress](/crosswatch/settings/configure-pairs/features/progress)

### Details by topic

* Safety switches and guardrails: [Global settings](/crosswatch/settings/configure-pairs/global-settings)
* Pair-scoped provider behavior: [Provider settings](/crosswatch/settings/configure-pairs/provider-settings)
* Pair-only library scoping: [Pair-level Whitelisting](/crosswatch/settings/configure-pairs/provider-settings/pair-level-whitelisting)
* AniList-specific pair controls: [AniList pair options](/crosswatch/settings/configure-pairs/anilist-pair-options)
* Watchlist controls and Jellyfin/Emby mode: [Watchlist](/crosswatch/settings/configure-pairs/features/watchlist)
* Ratings scope and backfill window: [Ratings](/crosswatch/settings/configure-pairs/features/ratings)
* History behavior and safety limitations: [History](/crosswatch/settings/configure-pairs/features/history)
* Resume position syncing (compatible provider pairs): [Progress](/crosswatch/settings/configure-pairs/features/progress)
* Runtime additions, removals, and conflicts: [Synchronization policies](/getting-started/support-matrix/pair-synchronization/synchronization-policies)

### When a section is disabled

Dependent controls lock.

This prevents accidental changes.

### Related topics

* [Pair synchronization](/getting-started/support-matrix/pair-synchronization)
* [Best practices](/getting-started/best-practices)


# Global settings

Pair-wide safety switches and advanced guardrails.

Global settings apply to the **whole pair**.

They control planning safety, write verification, and delete propagation.

### Settings

{% hint style="success" %}
Normally it’s not needed to change any settings here. The defaults are just fine for most users.
{% endhint %}

* **Dry run**: plan only. No writes.
* **Verify after write**: re-check a small sample after writes.
* **Drop guard**: blocks destructive plans when the source snapshot is empty.
* **Allow mass delete**: allows bulk removals when needed.
* **One-Way Remove mode (Deletions based on Source)**: controls how deletes behave in **one-way** pairs.
* **Tombstone retention (days)**: keeps delete markers to avoid re-adding items.
* **Include observed deletes**: treats provider-side deletions as intentional.

<figure><img src="/files/2QpXAl8C6OMWoPTF0n7O" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Allow **Mass Delete** is enabled by default because it’s often needed for the very first sync.

Once your pairs are stable, you can disable it for extra safety.

You cannot enable both **Mass Delete** and **Drop guard** at the same time.
{% endhint %}

#### One-Way Remove mode (Deletions based on Source)

This setting controls how CrossWatch plans removals for **one-way** pairs.

It does nothing unless **Remove** is enabled for the feature.

Features that commonly use this:

* Watchlist deletes
* Ratings clears
* Progress clears (where supported)

**Source** (default)

* UI: **Deletions based on Source** = **ON**
* Config: `one_way_remove_mode = "source_deletes"`
* Deletes only when there is a known match between source and destination.

**Mirror**

* UI: **Deletions based on Source** = **OFF**
* Config: `one_way_remove_mode = "mirror"`
* Destination strictly follows source. This will delete destination-only items.

{% hint style="warning" %}
Mirror mode is destructive.

Use it only when you want strict mirroring. Normally you don't need this.
{% endhint %}

### Blackbox (advanced)

Blackbox is a staging area for uncertain changes.

Use it when:

* IDs are unstable.
* Providers “flap” (adds keep reappearing).
* You want a cooldown before applying removals.

<figure><img src="/files/vek6X5MwI9XSgvjuKY1n" alt=""><figcaption></figcaption></figure>

#### Blackbox settings

* **Enabled**
* **Pair-scoped**
* **Promote after (days)**
* **Unresolved days**
* **Cooldown (days)**


# Provider settings

Pair-scoped provider behavior (matching, watched-state quirks, and more).

Provider settings apply **only to this pair**.

They are separate from provider setup in [**Connections**](/crosswatch/settings/connections).

{% hint style="success" %}
Normally it's not needed to change any settings here. The defaults are just fine for most users.
{% endhint %}

For library scoping, use [Pair-level Whitelisting](/crosswatch/settings/configure-pairs/provider-settings/pair-level-whitelisting).

### Providers → Media servers

{% hint style="info" %}
These options show up only when your pair uses a media server provider.
{% endhint %}

{% tabs %}
{% tab title="Plex" %}
**Strict ID matching (per pair)**

This is now **on by default**.

CrossWatch matches items **only by IDs**.

Title/year fallback is off by default because it causes too many false positives.

Expect more “missing peers” if your library items lack external IDs.

Turn this off only if you accept the risk of wrong matches.

**Marked watched (per pair)**

When enabled, CrossWatch also treats Plex’s **“marked watched”** state as history.

Use this when you rely on the Plex checkmark and real play events.

{% hint style="warning" %}
This works only for the **Plex server owner**.

It does **not** work for Plex **Home** users (managed users) or shared **friends**.
{% endhint %}

This is **add-only**:

* Mark watched in Plex → can sync out.
* Unmark watched in Plex → CrossWatch will not “unwatch” on other services.

**Fallback GUID (experimental, per pair)**

When enabled, CrossWatch uses extra Plex GUID recovery to find external IDs.

It can sometimes recover IDs for items you **don’t have in your libraries anymore**, as long as Plex still has enough metadata in its database.

This can be slower.

It can also increase wrong matches on messy libraries.

{% hint style="warning" %}
Enable this **only for a first-run**.

Then disable it.

It adds extra Plex lookups.

It can significantly increase CPU/network load.
{% endhint %}

See: [Adapter: Plex](/crosswatch/settings/synchronization/media-servers/plex).
{% endtab %}

{% tab title="Jellyfin" %}
**Strict ID matching (per pair)**

This is now **on by default**.

CrossWatch matches items **only by IDs**.

Title/year fallback is off by default because it causes too many false positives.

Expect more “missing peers” if your Jellyfin metadata agents don’t populate IDs.

Turn this off only if you accept the risk of wrong matches.
{% endtab %}

{% tab title="Emby" %}
**Strict ID matching (per pair)**

This is now **on by default**.

CrossWatch matches items **only by IDs**.

Title/year fallback is off by default because it causes too many false positives.

Expect more “missing peers” if your Emby metadata agents don’t populate IDs.

Turn this off only if you accept the risk of wrong matches.
{% endtab %}
{% endtabs %}


# Pair-level Whitelisting

Limit libraries for a single pair and keep other pairs unaffected.

Pair-level whitelisting limits libraries for **one pair**.

Use it when you need exceptions:

* one pair should ignore “Kids” or “Anime”
* one pair should backfill only a small library

For the full picture (server-level + pair-level), use:\
[Library Whitelisting](/crosswatch/settings/connections/library-whitelisting).

### What pair-level whitelisting affects

Pair-level whitelisting is applied inside the pair settings.

Typical features:

* **History**
* **Ratings**
* **Progress**

It does not change other pairs.

{% hint style="info" %}
Scrobble filtering is **server-level only**.\
Scrobbling is event-driven (Watcher/Webhooks), not pair-driven.
{% endhint %}

### Configure pair-level whitelisting

1. Go to **Pairs**.
2. Edit a pair (example: Plex → SIMKL).
3. Open **Provider settings** (Plex/Jellyfin/Emby).
4. Click **Load Libraries** if needed.
5. Select libraries for **History**, **Ratings**, and/or **Progress**.
6. Save the pair.

Library selections are stored per feature.

Selecting a library for **History** does not automatically select it for **Progress**.

### How it interacts with server-level whitelisting

Think of it like:

* Server-level = maximum allowed scope
* Pair-level = per-pair subset

### Troubleshooting

**Insights dashboard shows too much**

Dashboards are based on orchestrator state.\
Prefer server-level whitelisting for global scoping.

**Pair-level selection doesn’t apply**

* Confirm the pair provider section has libraries selected for that feature.
* Re-run the pair once to refresh state.

**Reset and allow everything**

* Clear libraries in the pair for the feature.
* Save and re-run.


# Features

Choose a sync feature and start with the lowest-risk setup.

Feature tabs show up only when **both** providers support that feature.

Most users enable **one feature at a time**.

{% hint style="success" %}
The safest order is Watchlist, then Ratings, then History if needed.
{% endhint %}

### Quick start

* Start **one-way**.
* Enable **one** feature.
* Run **dry run** first.

Related: [Configure Pairs](/crosswatch/settings/configure-pairs).

{% hint style="info" %}
Watchlist is the safest first feature.

History is the riskiest.
{% endhint %}

{% hint style="warning" %}
Two-way adds risk across every feature.

Conflicts, duplicates, overwrites, and propagated deletes become more likely when both sides can write.
{% endhint %}

### Pick a feature

<table data-view="cards"><thead><tr><th>Feature</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td><strong>Watchlist</strong><br>Sync “plan to watch” items.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/KNnBRgpRsxjPp2egBawm">/spaces/3rh5THg1PdhVsBt3GALo/pages/KNnBRgpRsxjPp2egBawm</a></td></tr><tr><td><strong>Ratings</strong><br>Sync rating values and timestamps when available.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/g1Zud9UhBjNypsiziVe0">/spaces/3rh5THg1PdhVsBt3GALo/pages/g1Zud9UhBjNypsiziVe0</a></td></tr><tr><td><strong>History</strong><br>Sync watched or completed state.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/7m19m7dZVqYr1WaT3OjB">/spaces/3rh5THg1PdhVsBt3GALo/pages/7m19m7dZVqYr1WaT3OjB</a></td></tr><tr><td><strong>Progress</strong><br>Sync resume position between media servers.</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/n575tse3rRGlm0zePIAq">/spaces/3rh5THg1PdhVsBt3GALo/pages/n575tse3rRGlm0zePIAq</a></td></tr></tbody></table>

### Recommended patterns

{% columns %}
{% column %}

#### Lower risk

* Watchlist
* Ratings
* one-way pairs
  {% endcolumn %}

{% column %}

#### Higher risk

* History
* two-way writes
* remove operations
  {% endcolumn %}
  {% endcolumns %}

#### Watchlist

Good for one-way or two-way.

Start one-way anyway.

#### Ratings

Prefer one-way.

Two-way tends to create noise.

#### History

Best as a **one-time seed**.

Then disable History in the pair.

Use scrobbling for new plays.

Related: [Watcher](/crosswatch/settings/scrobbler/watcher).


# Watchlist

Configure watchlist syncing (adds, removes, and Jellyfin/Emby modes).

Watchlist is a set-like feature.

It’s the safest feature to start with.

### Controls

* **Enable**: toggles watchlist syncing.
* **Add**: add missing items to target.
* **Remove**: remove items missing on source.

<figure><img src="/files/VxoytP88agXh0Mx1gLce" alt=""><figcaption></figcaption></figure>

AniList pairs can show extra watchlist options.

See [AniList pair options](/crosswatch/settings/configure-pairs/anilist-pair-options).

### AniList watchlist behavior

Resolved items are added to AniList **Planning**.

Items blocked by **Anime-only sync** are skipped.

CW records unresolved watchlist items in local state.

That reduces repeated retries on later runs.

### Jellyfin/Emby watchlist mode

Jellyfin/Emby don’t have a native “watchlist”.

CW emulates it via:

* **Favorites** (recommended)
* **Playlist**
* **Collection**

{% hint style="info" %}
Prefer **Favorites** or **Collection**.

Avoid **Playlist** unless you need it.
{% endhint %}

### Nuvio watchlist behavior

Nuvio uses its **Library** as the CrossWatch watchlist.

Watchlist sync supports movies and shows. It does not support seasons or episodes.

Enable removals only after validating a dry run. A removal deletes the title from the Nuvio Library.

### Related

* Overview of feature choices and patterns: [Features](/crosswatch/settings/configure-pairs/features)
* Pair-level feature toggles and safety: [Configure Pairs](/crosswatch/settings/configure-pairs)


# Ratings

Configure ratings sync, scope, and backfill windows.

Ratings syncs rating values.

Some providers also track **rated\_at**.

### Controls

* **Enable**
* **Add / Update**
* **Remove (clear)**

Scope:

* **All**
* or only **Movies**, **Shows**, **Episodes**

<figure><img src="/files/iPKuRziC4y35oGASb9Fj" alt=""><figcaption></figcaption></figure>

AniList pairs can show extra ratings options.

See [AniList pair options](/crosswatch/settings/configure-pairs/anilist-pair-options).

### AniList ratings behavior

CrossWatch uses the same ID resolution process before it writes or clears a rating.

With **Anime-only sync** on, ratings without usable AniList or MAL IDs are skipped.

This applies to both rating updates and rating clears.

### Backfill window

* **All**: backfill everything
* **From a date**: backfill only recent ratings

{% hint style="warning" %}
On some providers, rating a movie can affect completion state.

Use a narrow date window for large backfills.
{% endhint %}

### Related

* Overview of feature choices and patterns: [Features](/crosswatch/settings/configure-pairs/features)
* Pair-level feature toggles and safety: [Configure Pairs](/crosswatch/settings/configure-pairs)


# History

Configure watched-state syncing and safety limitations.

History syncs watched/completed state.

Use it carefully.

Most setups do a one-way seed once.

Then they rely on real-time scrobbling.

### Controls

* **Enable**
* **Add**
* **Remove**

<figure><img src="/files/BVsdGakcvGWIHGhjSaWc" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
History removes should normally be disabled to prevent accidental data loss.
{% endhint %}

### Nuvio history behavior

Nuvio supports history for movies and episodes.

Removing history clears the watched state. It does not remove the title from the Nuvio Library.

### Related

* Preserve every play of a title: [Rewatches](/crosswatch/settings/configure-pairs/features/history/rewatches)
* Overview of feature choices and patterns: [Features](/crosswatch/settings/configure-pairs/features)
* Pair-level feature toggles and safety: [Configure Pairs](/crosswatch/settings/configure-pairs)
* Real-time plays after the first seed: [Watcher](/crosswatch/settings/scrobbler/watcher)


# Rewatches

Preserve separate watch events for repeated plays.

Rewatches keep repeated plays as separate history events.

Normal history stores watched state and the latest watched time. Rewatches preserve every individual play.

For example, three plays of one movie remain three history events.

### Supported providers

Rewatches currently work with:

1. Trakt
2. SIMKL
3. PublicMetaDB
4. Floppy
5. MDBList
6. Scrob
7. CW Tracker

Each side of the connection must support the required capability.

For a one-way connection, the source must read rewatch events. The target must read and write them.

For a two-way connection, both providers must read and write rewatch events.

Normal history synchronization remains available without this support.

### Enable Rewatches

Configure Rewatches for each connection.

1. Open the connection.
2. Select **History**.
3. Enable **History**.
4. Enable **Rewatches**.
5. Save the connection.

CrossWatch then compares individual watch events. It no longer treats all plays as one history entry.

Each play keeps its own watched timestamp. Supported providers preserve repeated watches during synchronization.

### SIMKL requirements

SIMKL supports Rewatches with **Pro** and **VIP** accounts.

Without either account level, SIMKL cannot synchronize Rewatches.

### When to enable it

Enable Rewatches when repeated watches should remain in your tracker history.

Use normal History when watched state alone is enough.

Rewatches affect **History** only. They do not affect Watchlist, Ratings, or Playback Progress.


# Playlists

Configure playlist and collection mappings, membership safety, and ordering where supported.

Playlist and collection synchronization uses explicit endpoint mappings.

Choose one Source endpoint and one or more Destination endpoints. Support varies by provider and resource type.

### Membership mode

Choose the lowest-risk mode that fits the mapping:

* **Add only** adds missing Source items.
* **Managed only** removes only items managed by this mapping.
* **Mirror** makes Destination membership match the Source.

{% hint style="warning" %}
Mirror mode can remove manually added Destination items.
{% endhint %}

### Order and limits

Use **Ignore** to synchronize membership only. Use **Preserve** to request Source order where the Destination supports reordering.

Smart and read-only resources cannot be mapping targets. Large removals can be blocked unless the mapping allows mass deletion.

### Related

* Policy and provider behavior: [Synchronization policies](/getting-started/support-matrix/pair-synchronization/synchronization-policies)
* Feature selection and rules: [Features](/crosswatch/settings/configure-pairs/features)


# Progress

Sync resume position (“Continue Watching”) between supported providers.

Progress syncs **resume position**.

It keeps “Continue Watching” aligned across supported providers.

It does **not** mark items watched.

Use [History](/crosswatch/settings/configure-pairs/features/history) for watched state.

{% hint style="warning" %}
Progress is **experimental** and **best-effort**.

Resume data is not equally reliable across providers.

Plex is usually the least reliable for progress timestamps.

Start one-way and validate with a few titles.
{% endhint %}

Tracking issue: <https://github.com/cenodude/CrossWatch/issues/119>

{% tabs %}
{% tab title="End users" %}

### Supported pairs

The **Advanced Playback Progress** section appears only when both sides support progress sync.

Supported progress providers are:

* Plex
* Emby
* Jellyfin
* Nuvio
* Floppy
* PublicMetaDB
* CrossWatch local

### What syncs

Progress syncs the position where playback stopped.

It does **not** treat a missing resume position as a clear request.

Watched history stays separate from progress sync.

### Pair controls

Per pair, you can control:

* **Enable**
* **Mode** — one-way or two-way
* **Add / Update**
* **Remove (clear)**

Clear support depends on the destination provider.

**Emby** and **Jellyfin** support clears.

**Plex** does not support clears.

### Advanced playback progress

These settings live on the pair.

Different pairs can use different progress rules.

#### Minimum seconds

Default: `60`

Resume positions below this value are ignored.

A stop in the first minute does not sync with the default.

Use `0` to disable this filter.

#### Change threshold

Default: `30`

CrossWatch writes progress only when the source and destination differ by at least this value.

This reduces noisy writes from small player timing differences.

Use `0` to consider every difference.

#### Ignore near complete

Default: `95` percent

Positions at or above this value do not sync as unfinished progress.

This lets [History](/crosswatch/settings/configure-pairs/features/history) own the watched state.

Enable History if you rely on this setting.

#### Replay watched items

Default: disabled

By default, CrossWatch does not write unfinished progress over a watched destination item.

With this enabled, CrossWatch can mark the destination unwatched and restore the source resume point.

Use it only for deliberate rewatches.

#### Timestamp tolerance

Default: `30` seconds

Accepted range: `0` to `300` seconds

If the destination timestamp is newer than the source by more than this value, CrossWatch skips the write.

Increase it when minor clock drift blocks valid writes.

<details>

<summary>Change threshold examples</summary>

With a threshold of `30` seconds:

* Source `20:00`, destination `19:45` → no write
* Source `20:00`, destination `19:20` → write allowed

</details>

{% hint style="info" %}
Recommended defaults:

* Minimum seconds: `60`
* Change threshold: `30`
* Ignore near complete: `95`
* Replay watched items: disabled
* Timestamp tolerance: `30`
  {% endhint %}

<figure><img src="/files/BtTm1i8O4LuW8xSqlZKF" alt=""><figcaption></figcaption></figure>

### Progress vs Playback

**Progress** updates when the pair runs.

**Playback** reads the provider live.

The numbers can differ between those views.

That is normal.
{% endtab %}

{% tab title="Power users" %}

### Planning vs write scope

These settings affect planning for all supported progress pairs:

* **Minimum seconds**
* **Change threshold**
* **Ignore near complete**

These settings mainly affect writes to **Plex**, **Emby**, and **Jellyfin** destinations:

* **Replay watched items**
* **Timestamp tolerance**

**Nuvio**, **PublicMetaDB**, and **CrossWatch local** use their own write rules.

### Regression handling

When the source position is behind the destination, CrossWatch checks timestamps first.

Without usable timestamps, it does not move progress backward.

In two-way mode, the newest usable progress usually wins.

If timestamps are missing, the higher position wins.

### Clear behavior

Progress does **not** clear just because the source has no resume position.

Clear behavior still depends on pair remove settings and provider support.

{% hint style="warning" %}
One-way remove behavior uses **One-Way Remove mode (Deletions based on Source)** in [Global settings](/crosswatch/settings/configure-pairs/global-settings).

If you want source-side clears to mirror to the destination, you usually want **Mirror** mode.
{% endhint %}

Progress clears are tombstone-driven.

That means a clear can take two runs:

1. CrossWatch records that the source item disappeared.
2. The next run treats that disappearance as real and applies the clear.

See [Tombstones](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/AbAHapoU4fVH9SPBWr36).

### Additional write protection

Before writing to **Plex**, **Emby**, or **Jellyfin**, CrossWatch also skips when:

* the item is actively playing on the destination
* source and destination are the same provider instance
* the source has no usable progress timestamp
* the destination is newer than the allowed tolerance
* the destination is watched and replay mode is off
* the positions are already materially equal

These checks prevent scheduled syncs from overwriting newer destination activity.

### Provider notes

* **Plex** does not support clear
* **Plex** timestamps can be delayed or missing
* **Emby** clears by setting progress to `0`
* **Jellyfin** clears by setting progress to `0`
* **Nuvio** supports progress removal when pair removal is enabled
* **Replay watched items** is implemented for Plex, Emby, and Jellyfin destinations

### Related

* [Features](/crosswatch/settings/configure-pairs/features)
* [Configure Pairs](/crosswatch/settings/configure-pairs)
* [Playback](/crosswatch/main-dashboard/navigation/playback)
* [History](/crosswatch/settings/configure-pairs/features/history)
  {% endtab %}
  {% endtabs %}


# AniList pair options

Pair-level AniList options for Watchlist and Ratings connections.

AniList pairs can show extra options on **Watchlist** and **Ratings**.

Use these options when **AniList** appears on either side of the pair.

### AniList pair options

These options appear on **Watchlist** and **Ratings** when the pair contains **AniList**.

Each feature has its own settings.

Enabling mapping for **Watchlist** does not enable it for **Ratings**.

#### Use Anime ID Mapping

Anime services and general media services often use different IDs for the same title.

AniList mainly uses **AniList** and **MAL** IDs.

Plex, Jellyfin, Emby, and other providers often expose **TMDb**, **TVDb**, or **IMDb** IDs.

**Use Anime ID Mapping** enriches items before CrossWatch compares or writes them.

It can add:

* **AniList**
* **MAL**
* **AniDB**
* **TMDb**
* **TVDb**
* **IMDb**

This helps in both directions.

* **Plex → AniList**: CrossWatch can resolve AniList IDs before writes.
* **AniList → Plex**: CrossWatch can resolve media-server IDs before compares and writes.

Enabling this option can also enable the global Anime ID Mapping service when needed.

Disabling it for one feature disables it only for that feature on that pair.

Guide: [Anime ID Mapping](/crosswatch/settings/connections/metadata/anime-id-mapping)

#### How resolution works

Before CrossWatch compares or writes an item, it reads the IDs supplied by the source provider.

CrossWatch then queries the local AniBridge mapping database and adds any related IDs it can find.

When CrossWatch writes to AniList, it uses this order:

1. Existing or mapped **AniList** ID
2. Existing or mapped **MAL** ID, resolved through AniList
3. AniList title search

Title search is used only when **Anime-only sync** is off.

#### Anime-only sync

**Anime-only sync** limits what CrossWatch can write to AniList.

When enabled, CrossWatch accepts only items with a usable **AniList** or **MAL** ID after enrichment.

Items that would need title search are skipped.

Use it when AniList can receive data.

* **One-way** with AniList as **Target**
* **Two-way** pairs

It is not needed for **one-way** pairs from AniList.

When **Use Anime ID Mapping** is on and AniList can receive data, **Anime-only sync** is on by default for new pairs.

Disabling **Use Anime ID Mapping** also disables **Anime-only sync**.

#### Option combinations

* **Mapping off**: CrossWatch uses IDs already on the source. Title search can still run.
* **Mapping on, Anime-only off**: CrossWatch enriches IDs first. Title search can still run.
* **Mapping on, Anime-only on**: CrossWatch enriches IDs first. Items without AniList or MAL IDs are skipped.

#### Local mapping database

CrossWatch downloads the AniBridge dataset and builds a local SQLite index.

Normal sync lookups run locally.

CrossWatch does not contact AniBridge for every item.

Files are stored under:

`/config/.cw_cache/anime_mapping/anibridge/v3/`

Automatic updates are on by default.

CrossWatch checks for updates every `24` hours.

#### Limitations

Anime ID Mapping improves matching.

It does not guarantee coverage for every anime title, edition, or regional variant.

It also does not rename titles or change metadata in your media library.

{% hint style="warning" %}
Current behavior: if the local mapping database is unavailable, CrossWatch logs a warning and falls back to normal resolution.

That means **Anime-only sync** is not enforced during that fallback.
{% endhint %}

### Feature-specific behavior

* **Watchlist**: resolved items go to AniList **Planning**. See [Watchlist](/crosswatch/settings/configure-pairs/features/watchlist).
* **Ratings**: the same resolution flow applies to rating writes and clears. See [Ratings](/crosswatch/settings/configure-pairs/features/ratings).

### Related

* Pair setup overview: [Configure Pairs](/crosswatch/settings/configure-pairs)
* Global mapping service: [Anime ID Mapping](/crosswatch/settings/connections/metadata/anime-id-mapping)


# Scrobbler

Configure real-time playback scrobbling from your media server to a tracker.

Configure real-time playback scrobbling from Plex, Jellyfin, or Emby.

CrossWatch sends activity to Trakt, SIMKL, or MDBList while you watch.

{% hint style="success" %}
Use Watcher for most new setups.
{% endhint %}

CrossWatch supports two event sources:

* **Watcher** reads playback activity from the media server.
* **Webhooks** receive activity at a generated CrossWatch URL.

Watcher suits most installations. Webhooks remain fully supported.

{% tabs %}
{% tab title="End users" %}

<figure><img src="/files/BVGgn3Ri6uziEWT0YFEC" alt=""><figcaption></figcaption></figure>

### What Scrobbler does

Scrobbler processes playback activity as you watch.

It can send playback start, progress, pause, stop, and completion.

Plex ratings can also sync when enabled separately.

Use synchronization for history backfills and periodic alignment.

### Supported providers

**Media server sources**

* Plex
* Jellyfin
* Emby

**Tracker destinations**

* Trakt
* SIMKL
* MDBList

Connect both profiles under **Settings → Connections** before continuing.

### Recommended first setup

1. Connect one media server profile.
2. Connect one tracker profile.
3. Open **Settings → Scrobbler**.
4. Select **Add watcher**.
5. Choose the source and destination profiles.
6. Leave **Filters** empty for the first test.
7. Save the route.
8. Enable **Autostart** if needed.
9. Start playback.
10. Confirm the tracker receives activity.

Add filters and routes after the first successful test.

### Choose an event source

Use **Watcher** unless you specifically prefer server-delivered events.

See [Webhook or Watcher](/crosswatch/settings/scrobbler/webhook-or-watcher) for guidance.

{% hint style="warning" %}
Do not send identical playback activity through both sources.

It can create duplicate or competing scrobbles.
{% endhint %}

### Related guides

* [Watcher](/crosswatch/settings/scrobbler/watcher)
* [Webhooks](/crosswatch/settings/scrobbler/webhooks)
* [Recent Activity](/crosswatch/settings/scrobbler/recent-activity)
  {% endtab %}

{% tab title="Power users" %}

### The Scrobbler page

Open **Settings → Scrobbler**.

#### Summary

The summary shows active webhooks, enabled Watcher routes, and Watcher status.

#### Webhooks

Each card represents one mapping:

`media server profile → tracker profile`

Cards show the source, destination, endpoint status, and available actions.

Possible statuses are:

* **Connected** — the mapping is active.
* **Needs setup** — part of the mapping is no longer configured.
* **Disabled** — the webhook source profile is disabled.

#### Watcher routes

Each card represents one route:

`media server profile → tracker profile`

Cards show the source, destination, route ID, filters, enabled state, and runtime state.

Identical routes are not allowed. Source and destination providers and profiles must differ.

#### Watcher status

This section shows running and configured routes, Autostart, active sources and destinations, and runtime errors.

Use **Start**, **Stop**, or **Reload** to control Watcher.

Reload restarts groups from the saved route configuration.

#### Defaults

Defaults only appear when relevant:

* Watcher defaults require an enabled Watcher route.
* Webhook defaults require an enabled webhook.
* Plex ratings require an enabled Plex Watcher route.

### Entry-driven configuration

Scrobbler has no global mode selector.

An enabled Watcher route activates Watcher. An enabled webhook mapping activates Webhooks.

Removing or disabling the final entry deactivates that source.

Use **Save changes**, a toggle, or a Scrobbler control to save. Entries do not use the old global save flow.

### Using Watcher and Webhooks together

Both sources can run together when their responsibilities are separate.

For example:

* Watcher: Plex profile A → Trakt profile A
* Webhook: Jellyfin profile B → SIMKL profile B

Review [Webhook or Watcher](/crosswatch/settings/scrobbler/webhook-or-watcher) before enabling both.

### Global defaults

#### Watcher defaults

**Auto remove from Watchlists** removes completed movies from compatible watchlists.

Each route can inherit the global value or force it on or off.

**Pause debounce seconds** defaults to `5`. Repeated pauses inside this period are ignored.

**Suppress start percent** defaults to `99`. Starts at or above this progress are ignored.

#### Webhook defaults

**Pause debounce seconds** defaults to `5`.

**Suppress start percent** defaults to `99`.

Mappings inherit these values unless their source profile overrides them.

### Plex ratings

Playback scrobbling and Plex ratings use separate event flows.

#### Global ratings webhook

Use the global webhook when enabled Plex Watcher routes share rating destinations.

Select Trakt, SIMKL, MDBList, or any combination. Then copy the generated URL into Plex.

#### Route-specific ratings webhook

Use a route-specific webhook when one route needs different destinations.

Global and route-specific ratings are additive. Avoid the same destination in both paths.

Regenerating a ratings URL invalidates the previous URL.

### Legacy webhook cleanup

Older versions created separate Plex, Jellyfin, and Emby webhook URLs.

After every server uses the new profile URL:

1. Test playback through the new URL.
2. Confirm the correct webhook card activates.
3. Select **Remove legacy URLs**.
4. Remove old URLs from the media server.

{% hint style="danger" %}
Cleanup is permanent. Removed legacy tokens cannot be recreated.
{% endhint %}

### Related guides

* [Watcher](/crosswatch/settings/scrobbler/watcher)
* [Webhooks](/crosswatch/settings/scrobbler/webhooks)
* [Filters](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/PH2jfcZjseHr6sFkoKAP)
  {% endtab %}
  {% endtabs %}


# Webhook or Watcher

Choose how CW receives playback activity from your media server.

Choose how CW receives playback activity from Plex, Jellyfin, or Emby.

Both sources can scrobble to Trakt, SIMKL, and MDBList. They differ in how events reach CW.

Floppy and Stremio do not support Watcher or webhook playback sources.

### Recommendation

Use **Watcher** unless you have a specific reason to use Webhooks.

Watcher is managed in CW. It needs no inbound webhook URL and provides runtime controls.

Use **Webhooks** when the media server should push events to CW. They also suit reliable existing webhook deployments.

### Watcher

Watcher reads playback activity from a configured media server profile.

#### Use Watcher when

* CW can reach the media server API.
* You want the simplest setup.
* You need Start, Stop, Reload, or Autostart controls.
* You use several source or destination profiles.
* You need route-specific filters or completion options.
* You do not want an exposed webhook endpoint.

#### How Watcher runs

Each route is one mapping:

`source profile → destination profile`

CW groups enabled routes by source profile.

For example, these routes share one Plex watcher:

* Plex Home → Trakt Personal
* Plex Home → SIMKL Personal
* Plex Home → MDBList Personal

Each route still has its own destination, filters, and options.

#### Watcher considerations

* CW must stay running.
* The media server API must be reachable from CW.
* Emby and Jellyfin detection uses session polling.
* Stopped Watcher routes process nothing until restarted.
* Enable Autostart to resume after a CW restart.

### Webhooks

Webhooks let the media server send playback events to a generated CW URL.

#### Use Webhooks when

* The media server can reach the CW URL.
* The server or plugin delivers reliable playback events.
* Push delivery is preferred over continuous monitoring.
* A trusted reverse proxy provides a stable endpoint.
* You need a provider-specific webhook workflow.

#### How Webhooks run

A media server profile receives one generated endpoint URL.

That profile can forward events to one or more tracker destinations.

CW shows each destination as a separate card. The source uses one shared endpoint URL.

#### Webhook considerations

* The URL must be reachable from the media server.
* `localhost` points to the media server, not usually CW.
* Proxies, TLS, firewalls, and container networking can block delivery.
* Media servers may reject self-signed certificates.
* Regenerating the URL immediately invalidates the previous URL.
* Source-profile filters and options affect every attached destination.

### Feature comparison

#### Setup

**Watcher**

* Create a route in CW.
* Start Watcher.
* No media server webhook configuration is needed.

**Webhooks**

* Create a mapping in CW.
* Copy the generated URL.
* Configure that URL in the media server or plugin.

#### Network direction

**Watcher:** `CW → media server`

**Webhooks:** `media server → CW`

#### Runtime management

Watcher provides **Start**, **Stop**, **Reload**, **Autostart**, and route status.

Webhooks have no separate runtime service. They activate with a valid source, destination, and endpoint.

#### Filtering scope

Watcher filters are stored per route.

Webhook filters are stored per source profile. They affect every attached destination.

#### Per-destination behavior

Watcher supports route-specific filters, watchlist removal, watched thresholds, final-stop trust, and Plex ratings URLs.

Webhook playback options are shared by source profile. Mappings can still use different tracker profiles.

#### External requirements

Watcher needs no inbound URL or media server webhook feature.

Webhooks require a media server webhook feature or plugin. Availability and licensing differ by provider.

### Can both be enabled?

Yes. Watcher and Webhooks can run together.

{% hint style="warning" %}
Do not send the same source playback to the same tracker profile through both methods.

Duplicate scrobbles are easy to create.
{% endhint %}

### Safe hybrid examples

#### Separate media servers

* Watcher: Plex → Trakt
* Webhook: Jellyfin → SIMKL

#### Separate tracker profiles

* Watcher: Plex Personal → Trakt Personal
* Webhook: Plex Family → Trakt Family

#### Separate responsibilities

* Watcher handles playback scrobbles.
* A Plex ratings webhook handles ratings.

### Avoid these combinations

* Watcher: Plex Home → Trakt Personal
* Webhook: Plex Home → Trakt Personal

The same title can send overlapping start, pause, stop, and completion events.

Avoid simultaneous playback from several users to one tracker profile. Most tracker accounts represent one active viewer.

### Decision guide

Choose Watcher when any statement applies:

1. You want the recommended configuration.
2. You lack a trusted inbound URL.
3. You need runtime controls.
4. You need route-specific filters or options.
5. You use several profiles.

Choose Webhooks when any statement applies:

1. The media server already delivers reliable webhooks.
2. CW cannot reliably monitor the media server.
3. Push delivery is an operational requirement.
4. CW has a trusted reverse proxy endpoint.

### Migration from old webhooks

New webhook configuration uses profile-based URLs.

1. Create a webhook mapping for the source profile.
2. Copy its generated profile URL.
3. Replace the old URL in the media server.
4. Test playback.
5. Remove legacy URLs from CW.
6. Remove remaining old media server entries.

### Related pages

* [Scrobbler](/crosswatch/settings/scrobbler)
* [Webhooks](/crosswatch/settings/scrobbler/webhooks)
* [Watcher](/crosswatch/settings/scrobbler/watcher)
* [Filters](broken://spaces/3rh5THg1PdhVsBt3GALo/pages/PH2jfcZjseHr6sFkoKAP)




---

[Next Page](/llms-full.txt/1)

