> For the complete documentation index, see [llms.txt](https://wiki.crosswatch.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wiki.crosswatch.app/getting-started/support-matrix/pair-synchronization/synchronization-policies.md).

# Synchronization policies

{% 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.

**Collection** copies Source collection items to the Destination. It is one-way only.

### 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.md) for pair setup and [Global settings](/crosswatch/settings/configure-pairs/global-settings.md) 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.

#### Collection

Collection is always one-way. Enabling it locks the pair to **One-way** mode.

The first run creates a baseline. Later runs process deltas. Additions and removals use the feature controls.

For media servers, Collection means items from selected libraries. It does not use named server collections.

#### 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
  collection:
    add: true
    remove: false
    types: [movie, show, season, episode]
```

### 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 %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://wiki.crosswatch.app/getting-started/support-matrix/pair-synchronization/synchronization-policies.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
