> 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/crosswatch/advanced-and-api/configuration-reference.md).

# Configuration reference

Use this as a reference for `config.json`.

Defaults and normalization come from `cw_platform/config_base.py`.

Need the raw, generated default JSON blocks? See [Default config values](/crosswatch/advanced-and-api/configuration-reference/default-config-values.md).

{% hint style="warning" %}
`config.json` contains credentials, account identifiers, webhook route IDs, password hashes, and other sensitive configuration.

Sensitive values are encrypted when CW saves the configuration.

Encrypted values use the `enc:v1:` prefix.
{% endhint %}

CW obtains the encryption key from one of these locations, in priority order:

1. `CW_CONFIG_KEY`
2. `CROSSWATCH_CONFIG_KEY`
3. `<CONFIG_BASE>/.cw_master_key`

Back up `.cw_master_key` together with `config.json`.

An encrypted config cannot be recovered without the matching key.

Do not share `config.json`, `.cw_master_key`, provider tokens, or unredacted config output.

See [Config encryption](/crosswatch/advanced-and-api/configuration-reference/config-encryption.md) for the complete key management and recovery process.

{% hint style="info" %}
Avoid editing `config.json` by hand.

Most settings are configurable in the UI.
{% endhint %}

{% hint style="success" %}
Use this page as a reference, not as a setup checklist.
{% endhint %}

### Related config docs

<table data-view="cards"><thead><tr><th>Topic</th><th data-card-target data-type="content-ref">Open</th></tr></thead><tbody><tr><td>Generated defaults</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/KaMtaAGBjgDvoh2g6LxO">/spaces/3rh5THg1PdhVsBt3GALo/pages/KaMtaAGBjgDvoh2g6LxO</a></td></tr><tr><td>Secret storage</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/pzmuNfCTULe3Xev3N7q6">/spaces/3rh5THg1PdhVsBt3GALo/pages/pzmuNfCTULe3Xev3N7q6</a></td></tr><tr><td>Provider pacing</td><td><a href="/spaces/3rh5THg1PdhVsBt3GALo/pages/uIdojSHyuBmDdMI0yPFg">/spaces/3rh5THg1PdhVsBt3GALo/pages/uIdojSHyuBmDdMI0yPFg</a></td></tr></tbody></table>

### At a glance

| Topic          | Where                       | Notes                    |
| -------------- | --------------------------- | ------------------------ |
| Config file    | `<CONFIG_BASE>/config.json` | Created if missing.      |
| Docker default | `/config/config.json`       | Used when `/app` exists. |
| Manual edits   | Avoid                       | UI covers most settings. |
| Secrets        | Tokens and password hashes  | Redact before sharing.   |

### Config file location

CW chooses the config base directory in this order.

<table><thead><tr><th width="107.33331298828125">Priority</th><th>Condition</th><th>Base path</th></tr></thead><tbody><tr><td>1</td><td><code>CONFIG_BASE</code> env var is set</td><td><code>CONFIG_BASE</code> value</td></tr><tr><td>2</td><td>Running in Docker image (<code>/app</code> exists)</td><td><code>/config</code></td></tr><tr><td>3</td><td>Running from source</td><td>Project root (one level above <code>cw_platform/</code>)</td></tr></tbody></table>

The file path is always:

* `<CONFIG_BASE>/config.json`

### Load and save behavior

CW loads `config.json`, normalizes it, and rewrites managed values when needed.

#### Defaults and deep merge

CW loads `config.json` and deep merges it over `DEFAULT_CFG`.

* Dictionary values merge recursively.
* Other values replace their defaults.
* Unknown keys are retained.
* Missing default keys are restored during loading.

#### Version stamping

`version` is managed by CW and rewritten during save operations.

The value is selected in this order:

1. `api.versionAPI.CURRENT_VERSION`
2. `APP_VERSION`
3. Fallback `v0.7.0`

A leading `v` is removed before the value is stored.

#### Secret encryption

Sensitive string values are encrypted before CW writes the config to disk.

Encrypted values use the `enc:v1:` prefix.

Values are decrypted in memory when CW loads the file.

Sensitive fields include:

1. API keys
2. Access and refresh tokens
3. Client secrets
4. Plex tokens and home PINs
5. Session IDs and token hashes
6. Password hashes and salts
7. Device and request tokens
8. Webhook secrets and webhook route IDs

#### Atomic saves

CW writes a temporary JSON file, then replaces `config.json`.

This reduces the chance of a partial write after an interrupted save.

### Runtime normalization

Config values are normalized during load and save.

The normalized result can differ from the value originally written to `config.json`.

#### Webhook route IDs

CW generates a new route ID when one of these values is missing, is not a string, or has fewer than 16 characters:

1. `security.webhook_ids.plextrakt`
2. `security.webhook_ids.jellyfintrakt`
3. `security.webhook_ids.embytrakt`
4. `security.webhook_ids.plexwatcher`

Generated values use `token_urlsafe(24)`.

#### Scheduling

`scheduling.mode` is normalized to one of:

1. `disabled`
2. `hourly`
3. `every_n_hours`
4. `daily_time`
5. `custom_interval`

When `every_n_hours` is `1` or lower, the mode is changed to `hourly`.

`scheduling.daily_time` falls back to `03:30` when it is not a valid 24 hour `HH:MM` value.

`scheduling.custom_interval_minutes` has a minimum value of `15`.

#### Anime ID Mapping

`anime_mapping.refresh_hours` has a minimum value of `1`.

`anime_mapping.stale_after_days` has a minimum value of `1`.

An invalid `anime_mapping.release_tag` is replaced with `v3`.

Empty or invalid provider and feature lists are restored to:

```json
{
  "use_for_pairs": ["anilist"],
  "features": ["watchlist", "ratings"]
}
```

#### PublicMetaDB

PublicMetaDB page sizes, page limits, contribution limits, and request limits are clamped to supported ranges.

The default request limits are:

```json
{
  "post_per_sec": 3,
  "get_per_sec": 20
}
```

#### User interface

`ui.theme` is normalized to:

1. `flat-dark`
2. `flat-light`
3. `original`

Recent Activity and Recent Sync display values support:

1. `count:3`
2. `count:4`
3. `count:5`
4. `hours:24`
5. `hours:48`
6. `hours:72`

`ui.protocol` is normalized to `http` or `https`.

`ui.tls.valid_days` is clamped to `1` through `3650`.

#### UI authentication

UI authentication is mandatory.

`app_auth.enabled` is normalized to `true`, even when the raw default or a manually edited configuration contains `false`.

`app_auth.remember_session_days` is clamped to `1` through `365`.

When `reset_required` is enabled, active sessions and the previous login timestamp are cleared.

### Pair feature normalization

Feature configuration should use an object:

```json
{
  "enable": true,
  "add": true,
  "remove": false
}
```

When a feature object omits these fields, CW uses:

```json
{
  "enable": true,
  "add": true,
  "remove": false
}
```

A non-object feature value is treated as invalid and normalized to:

```json
{
  "enable": false,
  "add": false,
  "remove": false
}
```

Do not use `true` or `false` as the complete feature value in `config.json`.

#### Ratings feature fields

Allowed values are `movies`, `shows`, `seasons`, and `episodes`.

The value `all` expands to all four types.

An empty or invalid list falls back to `movies` and `shows`.

Allowed values are:

1. `only_new`
2. `from_date`
3. `all`

Used when `mode` is `from_date`.

It is cleared for other modes.

#### AniList feature fields

Watchlist and Ratings features on pairs containing AniList can contain:

**`use_anime_mapping`**

Uses the local AniBridge mapping index to enrich identifiers before comparison and writes.

Enabling it in the pair editor also enables the global Anime ID Mapping service.

**`anime_only_sync`**

This requires `use_anime_mapping`.

When AniList receives data, only items with a usable AniList or MyAnimeList ID are accepted.

Items that would require AniList title search are skipped.

The option is forced to `false` when Anime ID Mapping is disabled for the feature.

Example:

```json
{
  "watchlist": {
    "enable": true,
    "add": true,
    "remove": false,
    "use_anime_mapping": true,
    "anime_only_sync": true
  },
  "ratings": {
    "enable": true,
    "add": true,
    "remove": false,
    "types": ["movies", "shows"],
    "mode": "all",
    "from_date": "",
    "use_anime_mapping": true,
    "anime_only_sync": true
  }
}
```

More detail: [AniList pair options](/crosswatch/settings/configure-pairs/anilist-pair-options.md).

### Redaction

`redact_config(cfg)` replaces sensitive values with `••••••••`.

Redaction covers provider credentials in both the default provider block and provider profiles under `instances`.

Redacted provider values include credentials for:

1. Plex
2. Trakt
3. SIMKL
4. AniList
5. MDBList
6. PublicMetaDB
7. Tautulli
8. Jellyfin
9. Emby
10. TMDb
11. TMDb Sync

The following local authentication values are also redacted:

1. `app_auth.password.hash`
2. `app_auth.password.salt`
3. `app_auth.session.token_hash`
4. Token hashes inside `app_auth.sessions[]`
5. All values under `security.webhook_ids`

### Key reference

#### Top-level

| Key                | Type   | Notes                               |
| ------------------ | ------ | ----------------------------------- |
| `version`          | string | Managed by the app.                 |
| `security`         | object | Runtime-generated webhook IDs.      |
| `pairs`            | array  | Pair definitions created in the UI. |
| `plex`, `trakt`, … | object | Provider connection and tuning.     |
| `nuvio`            | object | Nuvio request pacing.               |
| `publicmetadb`     | object | PublicMetaDB auth and sync tuning.  |
| `sync`             | object | Global orchestrator defaults.       |
| `runtime`          | object | Logging and guardrails.             |
| `metadata`         | object | TMDb metadata lookup settings.      |
| `anime_mapping`    | object | Anime ID Mapping settings.          |
| `scrobble`         | object | Real-time progress forwarding.      |
| `scheduling`       | object | Periodic runs.                      |
| `ui`               | object | UI toggles.                         |
| `app_auth`         | object | Mandatory UI authentication.        |

### Security (`security`)

This node stores webhook route IDs.

CW can auto-generate them.

| Key                                  | Type   | Notes                         |
| ------------------------------------ | ------ | ----------------------------- |
| `security.webhook_ids.plextrakt`     | string | Plex webhook path ID.         |
| `security.webhook_ids.jellyfintrakt` | string | Jellyfin webhook path ID.     |
| `security.webhook_ids.embytrakt`     | string | Emby webhook path ID.         |
| `security.webhook_ids.plexwatcher`   | string | Plex Watcher/webhook path id. |

{% hint style="warning" %}
If you change these IDs, update your webhook URLs.

Restart CW after edits.
{% endhint %}

### Providers

| Provider            | Stores                       | Used for                              |
| ------------------- | ---------------------------- | ------------------------------------- |
| `plex`              | server URL, token, tuning    | Sync + scrobble source                |
| `jellyfin` / `emby` | server URL, token, tuning    | Sync + scrobble source                |
| `trakt` / `simkl`   | OAuth/API keys               | Tracker targets and sources           |
| `nuvio`             | Request pacing               | Nuvio provider requests               |
| `anilist`           | OAuth/API keys               | Bidirectional watchlist + ratings     |
| `mdblist`           | Device Code or API key auth  | Watchlist, ratings, and history       |
| `publicmetadb`      | API key and sync tuning      | Watchlist, ratings, history, progress |
| `tmdb`              | API key                      | Matching support                      |
| `tmdb_sync`         | API key + session            | TMDb account sync                     |
| `tautulli`          | URL + API key                | History import                        |
| `crosswatch`        | local paths + snapshot rules | Local backup provider                 |

#### Provider profiles

Most providers can store multiple profiles under an `instances` map (config key name).

* The implicit profile id is `default`.
* Additional profile ids are named like `PLEX-P01`, `TRAKT-P02`, etc.

Profiles are used by:

* sync pairs (`source_instance`, `target_instance`)
* Watcher routes (`provider_instance`, `sink_instance`)

Guide: [Profiles](/crosswatch/settings/connections/profiles.md)

<details>

<summary><strong>Plex</strong></summary>

#### Connection

| Key                  | Type   | Notes                                       |
| -------------------- | ------ | ------------------------------------------- |
| `plex.server_url`    | string | Example: `http://192.168.1.10:32400`        |
| `plex.verify_ssl`    | bool   | Keep `false` for self-signed.               |
| `plex.account_token` | string | Set via UI PIN login.                       |
| `plex.pms_token`     | string | Plex Media Server resource token.           |
| `plex.client_id`     | string | Client identifier.                          |
| `plex.machine_id`    | string | Targets a specific server.                  |
| `plex.username`      | string | Plex Home profile name.                     |
| `plex.account_id`    | string | Server-local account ID.                    |
| `plex.home_pin`      | string | Optional Plex Home PIN.                     |
| `plex.timeout`       | number | Seconds.                                    |
| `plex.max_retries`   | int    | Retry budget.                               |
| `plex.fallback_GUID` | bool   | Discover fallback for missing GUID mapping. |

#### Scrobble filters

| Key                       | Type  | Notes                                                                |
| ------------------------- | ----- | -------------------------------------------------------------------- |
| `plex.scrobble.libraries` | array | Shared by Plex Watcher and Plex Webhooks. Empty means all libraries. |

#### History

| Key                                   | Type  | Notes                                                                   |
| ------------------------------------- | ----- | ----------------------------------------------------------------------- |
| `plex.history.libraries`              | array | Whitelist of **library GUIDs**. Empty = all.                            |
| `plex.history.include_marked_watched` | bool  | Include Plex “marked watched” state (add-only behavior).                |
| `plex.history_workers`                | int   | Parallel workers for history indexing. `12–16` is ideal on a local NAS. |

#### Ratings

| Key                      | Type  | Notes                                                                   |
| ------------------------ | ----- | ----------------------------------------------------------------------- |
| `plex.ratings.libraries` | array | Whitelist of **library GUIDs**. Empty = all.                            |
| `plex.rating_workers`    | int   | Parallel workers for ratings indexing. `12–16` is ideal on a local NAS. |

#### Watchlist (Discover-driven)

| Key                                 | Type  | Notes                                                                                     |
| ----------------------------------- | ----- | ----------------------------------------------------------------------------------------- |
| `plex.watchlist_allow_pms_fallback` | bool  | Allow PMS watchlist fallback when needed. Keep `false` for strict Discover-only behavior. |
| `plex.watchlist_page_size`          | int   | Discover page size (`100–200`). Higher is faster, but can trigger 504s.                   |
| `plex.watchlist_query_limit`        | int   | Max Discover search results per query (`10–25`).                                          |
| `plex.watchlist_write_delay_ms`     | int   | Optional pacing between Discover writes. Set `50–150` if you hit 429/5xx.                 |
| `plex.watchlist_title_query`        | bool  | Use title/slug tokens for Discover candidate fetching.                                    |
| `plex.watchlist_use_metadata_match` | bool  | Try PMS `/library/metadata/matches` first, then fallback to Discover.                     |
| `plex.watchlist_guid_priority`      | array | GUID resolution order (first match wins).                                                 |

</details>

<details>

<summary><strong>TMDb</strong></summary>

| Key            | Type   | Notes                                                  |
| -------------- | ------ | ------------------------------------------------------ |
| `tmdb.api_key` | string | TMDb API key used for metadata lookup and ID bridging. |

</details>

<details>

<summary><strong>TMDb (Sync)</strong></summary>

Used only when you connect **TMDb (Sync)** for watchlist/ratings syncing.

| Key                     | Type   | Notes                                   |
| ----------------------- | ------ | --------------------------------------- |
| `tmdb_sync.api_key`     | string | TMDb v3 API key.                        |
| `tmdb_sync.session_id`  | string | User session ID from the connect flow.  |
| `tmdb_sync.account_id`  | string | Optional. Auto-discovered if missing.   |
| `tmdb_sync.timeout`     | number | HTTP timeout in seconds.                |
| `tmdb_sync.max_retries` | int    | Retry budget for transient HTTP errors. |
| `tmdb_sync.debug`       | bool   | Enables extra TMDb sync debug logging.  |

</details>

<details>

<summary><strong>Trakt</strong></summary>

#### OAuth

| Key                     | Type   | Notes                                   |
| ----------------------- | ------ | --------------------------------------- |
| `trakt.client_id`       | string | From your Trakt app.                    |
| `trakt.client_secret`   | string | From your Trakt app.                    |
| `trakt.access_token`    | string | OAuth2 access token.                    |
| `trakt.refresh_token`   | string | OAuth2 refresh token.                   |
| `trakt.scope`           | string | Usually `public` or `private`.          |
| `trakt.token_type`      | string | Usually `Bearer`.                       |
| `trakt.expires_at`      | int    | Epoch seconds.                          |
| `trakt._pending_device` | object | Temporary device code state (PIN flow). |

#### HTTP

| Key                 | Type | Notes                           |
| ------------------- | ---- | ------------------------------- |
| `trakt.timeout`     | int  | HTTP timeout (seconds).         |
| `trakt.max_retries` | int  | Retry budget (429/5xx backoff). |

#### Rate limiting

| Key                             | Type   | Notes                 |
| ------------------------------- | ------ | --------------------- |
| `trakt.rate_limit.get_per_sec`  | number | GET requests / second |
| `trakt.rate_limit.post_per_sec` | number | Writes / second       |

Rate-limit defaults and tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries.md)

#### Watchlist

| Key                                | Type | Notes                                                        |
| ---------------------------------- | ---- | ------------------------------------------------------------ |
| `trakt.watchlist_use_etag`         | bool | Use ETag + local shadow to skip unchanged lists.             |
| `trakt.watchlist_shadow_ttl_hours` | int  | Refresh ETag baseline periodically even if 304s keep coming. |
| `trakt.watchlist_batch_size`       | int  | Chunk size for add/remove to reduce rate spikes.             |
| `trakt.watchlist_log_rate_limits`  | bool | Log X-RateLimit-\* and Retry-After when present.             |
| `trakt.watchlist_freeze_details`   | bool | Persist last status & ids for debugging.                     |

#### Ratings

| Key                        | Type | Notes                                    |
| -------------------------- | ---- | ---------------------------------------- |
| `trakt.ratings_per_page`   | int  | Items per page when indexing (`10–100`). |
| `trakt.ratings_max_pages`  | int  | Safety cap per type.                     |
| `trakt.ratings_chunk_size` | int  | Batch size for POST/REMOVE.              |

#### History

| Key                             | Type | Notes                                               |
| ------------------------------- | ---- | --------------------------------------------------- |
| `trakt.history_per_page`        | int  | Max allowed by Trakt (`100`).                       |
| `trakt.history_max_pages`       | int  | Safety cap for large histories.                     |
| `trakt.history_unresolved`      | bool | Enable unresolved “freeze” files.                   |
| `trakt.history_number_fallback` | bool | Episode number fallback if episode IDs are missing. |
| `trakt.history_collection`      | bool | Mirror history adds into your Trakt Collection.     |

</details>

<details>

<summary><strong>SIMKL</strong></summary>

| Key                      | Type   | Notes                                   |
| ------------------------ | ------ | --------------------------------------- |
| `simkl.access_token`     | string | OAuth2 access token.                    |
| `simkl.refresh_token`    | string | OAuth2 refresh token.                   |
| `simkl.token_expires_at` | int    | Epoch seconds.                          |
| `simkl.client_id`        | string | From your SIMKL app.                    |
| `simkl.client_secret`    | string | From your SIMKL app.                    |
| `simkl.date_from`        | string | `YYYY-MM-DD` (optional backfill start). |

#### Rate limiting

| Key                             | Type   | Notes                 |
| ------------------------------- | ------ | --------------------- |
| `simkl.rate_limit.get_per_sec`  | number | GET requests / second |
| `simkl.rate_limit.post_per_sec` | number | Writes / second       |

Rate-limit defaults and tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries.md)

</details>

<details>

<summary><strong>Nuvio</strong></summary>

#### Rate limiting

| Key                             | Type   | Notes                 |
| ------------------------------- | ------ | --------------------- |
| `nuvio.rate_limit.get_per_sec`  | number | GET requests / second |
| `nuvio.rate_limit.post_per_sec` | number | Writes / second       |

Default limits are `100` GET requests and `100` write requests per second.

Rate-limit defaults and tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries.md)

</details>

<details>

<summary><strong>AniList</strong></summary>

#### Authentication

| Key                     | Type   | Notes                                                                        |
| ----------------------- | ------ | ---------------------------------------------------------------------------- |
| `anilist.client_id`     | string | Client ID from the AniList application.                                      |
| `anilist.client_secret` | string | Client secret from the AniList application.                                  |
| `anilist.access_token`  | string | OAuth access token.                                                          |
| `anilist.user`          | object | Cached AniList viewer information, normally containing the user ID and name. |

AniList Watchlist uses entries with the `PLANNING` status.

AniList Ratings supports movies and shows.

Seasons and episodes are not supported.

No additional provider-level Watchlist or Ratings tuning is currently required.

AniList does not currently support CW History, Progress, or Playlist sync.

Anime matching is configured globally under `anime_mapping` and per feature inside the pair.

</details>

<details>

<summary><strong>MDBList</strong></summary>

#### Authentication

MDBList supports Device Code authentication and legacy API key authentication.

New connections should use Device Code authentication.

| Key                     | Type   | Notes                                      |
| ----------------------- | ------ | ------------------------------------------ |
| `mdblist.auth_method`   | string | Normalized to `device_code` or `api_key`.  |
| `mdblist.client_id`     | string | Deprecated. CW supplies this internally.   |
| `mdblist.access_token`  | string | OAuth access token from Device Code auth.  |
| `mdblist.refresh_token` | string | OAuth refresh token from Device Code auth. |
| `mdblist.token_type`    | string | OAuth token type. Default is `Bearer`.     |
| `mdblist.scope`         | string | OAuth scope. Default is `write`.           |
| `mdblist.expires_at`    | int    | Access token expiry as Unix epoch seconds. |
| `mdblist.api_key`       | string | Legacy API key authentication.             |
| `mdblist.timeout`       | int    | HTTP timeout in seconds.                   |
| `mdblist.max_retries`   | int    | Retry budget.                              |

When Device Code auth is active, the API key is cleared.

When API key auth is active, OAuth token fields are cleared.

#### Rate limiting

| Key                               | Type   | Notes                 |
| --------------------------------- | ------ | --------------------- |
| `mdblist.rate_limit.get_per_sec`  | number | GET requests / second |
| `mdblist.rate_limit.post_per_sec` | number | Writes / second       |

Rate-limit defaults and tuning: [Provider rate limiting](/crosswatch/advanced-and-api/rate-limiting-and-retries.md)

#### Watchlist

| Key                                  | Type | Notes                                         |
| ------------------------------------ | ---- | --------------------------------------------- |
| `mdblist.watchlist_shadow_ttl_hours` | int  | Shadow TTL (hours). `0` disables.             |
| `mdblist.watchlist_shadow_validate`  | bool | Validate shadow on every run.                 |
| `mdblist.watchlist_page_size`        | int  | GET page size for `/watchlist/items`.         |
| `mdblist.watchlist_batch_size`       | int  | Batch size for add/remove writes.             |
| `mdblist.watchlist_freeze_details`   | bool | Store extra details for “not\_found” freezes. |

#### Ratings

| Key                              | Type   | Notes                                          |
| -------------------------------- | ------ | ---------------------------------------------- |
| `mdblist.ratings_per_page`       | int    | Items per page when indexing.                  |
| `mdblist.ratings_max_pages`      | int    | Max pages to fetch (safety cap).               |
| `mdblist.ratings_chunk_size`     | int    | Batch size for POST/REMOVE.                    |
| `mdblist.ratings_write_delay_ms` | int    | Optional pacing between writes.                |
| `mdblist.ratings_max_backoff_ms` | int    | Max backoff time for retries.                  |
| `mdblist.ratings_since`          | string | First-run baseline; watermark overrides after. |

#### History

| Key                              | Type   | Notes                                          |
| -------------------------------- | ------ | ---------------------------------------------- |
| `mdblist.history_per_page`       | int    | Items per page for watched delta.              |
| `mdblist.history_max_pages`      | int    | Max pages to fetch (safety cap).               |
| `mdblist.history_chunk_size`     | int    | Batch size for watched/unwatched writes.       |
| `mdblist.history_write_delay_ms` | int    | Optional pacing between writes.                |
| `mdblist.history_max_backoff_ms` | int    | Max backoff time for retries.                  |
| `mdblist.history_since`          | string | First-run baseline; watermark overrides after. |

</details>

<details>

<summary><strong>PublicMetaDB</strong></summary>

#### Connection

| Key                        | Type   | Notes                                                |
| -------------------------- | ------ | ---------------------------------------------------- |
| `publicmetadb.api_key`     | string | PublicMetaDB API key.                                |
| `publicmetadb.base_url`    | string | API base URL. Default is `https://publicmetadb.com`. |
| `publicmetadb.timeout`     | number | HTTP timeout in seconds.                             |
| `publicmetadb.max_retries` | int    | Retry budget.                                        |

#### Watchlist

| Key                                  | Type   | Notes                                                  |
| ------------------------------------ | ------ | ------------------------------------------------------ |
| `publicmetadb.watchlist_list_id`     | string | Optional list ID. Empty lets CW discover or create it. |
| `publicmetadb.watchlist_name`        | string | Default list name. Default is `Watchlist`.             |
| `publicmetadb.watchlist_auto_create` | bool   | Creates a private CW watchlist when needed.            |
| `publicmetadb.watchlist_page_size`   | int    | Normalized to `1` through `500`.                       |

#### History

| Key                              | Type | Notes                               |
| -------------------------------- | ---- | ----------------------------------- |
| `publicmetadb.history_per_page`  | int  | Normalized to `1` through `500`.    |
| `publicmetadb.history_max_pages` | int  | Normalized to `1` through `100000`. |

#### Progress

| Key                               | Type | Notes                               |
| --------------------------------- | ---- | ----------------------------------- |
| `publicmetadb.progress_per_page`  | int  | Normalized to `1` through `500`.    |
| `publicmetadb.progress_max_pages` | int  | Normalized to `1` through `100000`. |

#### Ratings

| Key                                    | Type   | Notes                               |
| -------------------------------------- | ------ | ----------------------------------- |
| `publicmetadb.ratings_label`           | string | Rating label. Default is `Overall`. |
| `publicmetadb.ratings_submit_per_hour` | int    | Maximum supported value is `200`.   |
| `publicmetadb.ratings_update_per_hour` | int    | Maximum supported value is `100`.   |

#### Request limiting

| Key                                    | Type   | Notes                           |
| -------------------------------------- | ------ | ------------------------------- |
| `publicmetadb.rate_limit.post_per_sec` | number | Normalized to `0` through `3`.  |
| `publicmetadb.rate_limit.get_per_sec`  | number | Normalized to `0` through `20`. |

</details>

<details>

<summary><strong>Tautulli</strong></summary>

#### Connection

| Key                    | Type   | Notes                       |
| ---------------------- | ------ | --------------------------- |
| `tautulli.server_url`  | string | Example: `http://host:8181` |
| `tautulli.api_key`     | string | Tautulli API key.           |
| `tautulli.verify_ssl`  | bool   | Verify TLS certificates.    |
| `tautulli.timeout`     | number | HTTP timeout (seconds).     |
| `tautulli.max_retries` | int    | Retry budget.               |

#### History import

| Key                          | Type   | Notes                       |
| ---------------------------- | ------ | --------------------------- |
| `tautulli.history.user_id`   | string | Optional user filter.       |
| `tautulli.history.per_page`  | int    | Tautulli history page size. |
| `tautulli.history.max_pages` | int    | Safety cap.                 |

</details>

<details>

<summary><strong>Jellyfin / Emby</strong></summary>

Same structure.

#### Connection

| Key              | Type   | Notes                                        |
| ---------------- | ------ | -------------------------------------------- |
| `*.server`       | string | Base URL.                                    |
| `*.access_token` | string | Auth token.                                  |
| `*.user_id`      | string | Active user.                                 |
| `*.device_id`    | string | Client device id.                            |
| `*.username`     | string | Optional login username.                     |
| `*.user`         | string | Optional display name (hydrated after auth). |
| `*.verify_ssl`   | bool   | TLS verification.                            |
| `*.timeout`      | number | Seconds.                                     |
| `*.max_retries`  | int    | Retry budget.                                |

#### Scrobble filters

| Key                    | Type  | Notes                                    |
| ---------------------- | ----- | ---------------------------------------- |
| `*.scrobble.libraries` | array | Whitelist of library GUIDs. Empty = all. |

#### Watchlist (emulated)

| Key                                    | Type   | Notes                                      |
| -------------------------------------- | ------ | ------------------------------------------ |
| `*.watchlist.mode`                     | string | `favorites` \| `playlist` \| `collections` |
| `*.watchlist.playlist_name`            | string | Used when mode is `playlist`.              |
| `*.watchlist.watchlist_query_limit`    | int    | Batch size.                                |
| `*.watchlist.watchlist_write_delay_ms` | int    | Delay between writes.                      |
| `*.watchlist.watchlist_guid_priority`  | array  | ID match order.                            |

#### History

| Key                                | Type  | Notes                                    |
| ---------------------------------- | ----- | ---------------------------------------- |
| `*.history.history_query_limit`    | int   | Batch size.                              |
| `*.history.history_write_delay_ms` | int   | Delay between writes.                    |
| `*.history.history_guid_priority`  | array | ID match order.                          |
| `*.history.libraries`              | array | Whitelist of library GUIDs. Empty = all. |

#### Ratings

| Key                             | Type  | Notes                                    |
| ------------------------------- | ----- | ---------------------------------------- |
| `*.ratings.ratings_query_limit` | int   | Ratings query limit (default `2000`).    |
| `*.ratings.libraries`           | array | Whitelist of library GUIDs. Empty = all. |

</details>

<details>

<summary><strong>CW (local provider)</strong></summary>

| Key                            | Type   | Notes                                         |
| ------------------------------ | ------ | --------------------------------------------- |
| `crosswatch.root_dir`          | string | Local provider folder.                        |
| `crosswatch.enabled`           | bool   | Enables provider.                             |
| `crosswatch.auto_snapshot`     | bool   | Snapshot before writes.                       |
| `crosswatch.retention_days`    | int    | `0` keeps forever.                            |
| `crosswatch.max_snapshots`     | int    | `0` unlimited.                                |
| `crosswatch.restore_watchlist` | string | `latest`, empty, or a specific snapshot stem. |
| `crosswatch.restore_history`   | string | `latest`, empty, or a specific snapshot stem. |
| `crosswatch.restore_ratings`   | string | `latest`, empty, or a specific snapshot stem. |

</details>

### Sync (`sync`)

Global defaults. Pair-level settings override these by design.

| Key                             | Type   | Notes                                                             |
| ------------------------------- | ------ | ----------------------------------------------------------------- |
| `sync.enable_add`               | bool   | Allow additions by default.                                       |
| `sync.enable_remove`            | bool   | Safer default: disabled unless you opt in.                        |
| `sync.one_way_remove_mode`      | string | One-way delete semantics: `source_deletes` (default) or `mirror`. |
| `sync.verify_after_write`       | bool   | When supported, re-check destination after writes.                |
| `sync.dry_run`                  | bool   | Plan and log only. No writes.                                     |
| `sync.drop_guard`               | bool   | Guard against empty/suspect snapshots.                            |
| `sync.allow_mass_delete`        | bool   | If `false`, block large delete plans.                             |
| `sync.tombstone_ttl_days`       | int    | How long “observed deletes” stay valid.                           |
| `sync.include_observed_deletes` | bool   | If `false`, skip processing observed deletes for the run.         |

#### One-way delete modes

This setting matters only for **one-way** pairs.

It is ignored for **two-way** sync.

It is also gated by remove toggles:

* Global: `sync.enable_remove`
* Pair feature: `pairs[].features.<feature>.remove`

If either is `false`, no deletes happen.

**`source_deletes` (default)**

Safest behavior.

CW removes items from the destination only when the item looks like a **real deletion on the source**.

That avoids deleting:

* destination-only items
* items you manually added on the destination

**`mirror`**

Strict mirroring.

CW removes anything present on the destination but missing on the source.

This is destructive.

**Example (Python-style)**

```python
"sync": {
    # Global write gates (pair/feature settings will override these by design):
    "enable_add": True,                             # Allow additions by default
    "enable_remove": False,                         # Safer default: do not remove items unless explicitly enabled
    "one_way_remove_mode": "source_deletes",        # "source_deletes" | "mirror" (mirror = destructive; use with care)
}
```

**Force mirroring (JSON)**

Set:

```json
"sync": {
  "one_way_remove_mode": "mirror"
}
```

{% hint style="warning" %}
`mirror` can delete destination-only items.

Use it only if you really want strict mirroring.
{% endhint %}

#### Two-way defaults (optional)

| Key                                  | Type   | Notes                                                 |
| ------------------------------------ | ------ | ----------------------------------------------------- |
| `sync.bidirectional.enabled`         | bool   | Default off. Pairs still decide final mode.           |
| `sync.bidirectional.mode`            | string | Placeholder default (usually `two-way`).              |
| `sync.bidirectional.source_of_truth` | string | Optional tie-breaker if you enforce strict authority. |

#### Blackbox (including flapper protection)

| Key                             | Type | Notes                                                      |
| ------------------------------- | ---- | ---------------------------------------------------------- |
| `sync.blackbox.enabled`         | bool | Turn off to fully disable blackbox logic.                  |
| `sync.blackbox.promote_after`   | int  | Promote after N consecutive unresolved/fail events.        |
| `sync.blackbox.unresolved_days` | int  | Minimum unresolved age before it counts (`0` = immediate). |
| `sync.blackbox.pair_scoped`     | bool | Track per pair to avoid blocking the same title elsewhere. |
| `sync.blackbox.cooldown_days`   | int  | Auto-prune after cooldown.                                 |
| `sync.blackbox.block_adds`      | bool | Block planned ADDs while blackboxed.                       |
| `sync.blackbox.block_removes`   | bool | Block planned REMOVEs while blackboxed.                    |

### Runtime (`runtime`)

| Key                                    | Type   | Notes                                                  |
| -------------------------------------- | ------ | ------------------------------------------------------ |
| `runtime.debug`                        | bool   | Extra verbose logging (debug level).                   |
| `runtime.debug_http`                   | bool   | Extra verbose HTTP logging.                            |
| `runtime.debug_mods`                   | bool   | Extra verbose MOD logs for Synchronization Providers.  |
| `runtime.state_dir`                    | string | Optional override for state dir. Avoid for containers. |
| `runtime.telemetry.enabled`            | bool   | Usage stats.                                           |
| `runtime.snapshot_ttl_sec`             | int    | Reuse snapshots within this window.                    |
| `runtime.apply_chunk_size`             | int    | Batch size for apply.                                  |
| `runtime.apply_chunk_pause_ms`         | int    | Pause between chunks.                                  |
| `runtime.apply_chunk_size_by_provider` | object | Per-provider overrides (example: `SIMKL`).             |
| `runtime.suspect_min_prev`             | int    | Minimum previous size to enable suspect guard.         |
| `runtime.suspect_shrink_ratio`         | number | Shrink ratio to trigger suspect guard.                 |

### Metadata (`metadata`)

| Key                  | Type   | Notes                      |
| -------------------- | ------ | -------------------------- |
| `metadata.locale`    | string | Example: `en-US`.          |
| `metadata.ttl_hours` | int    | Coarse resolver cache TTL. |

### Anime ID Mapping (`anime_mapping`)

Anime ID Mapping enriches media identifiers for supported features on pairs containing AniList.

The dataset is downloaded from the AniBridge release repository.

It is not bundled with CW.

The cache path is:

```
<CONFIG_BASE>/.cw_cache/anime_mapping/anibridge/<release_tag>
```

#### Configuration

| Key                              | Type   | Notes                                                                  |
| -------------------------------- | ------ | ---------------------------------------------------------------------- |
| `anime_mapping.enabled`          | bool   | Enables the local Anime ID Mapping service. Default `false`.           |
| `anime_mapping.auto_update`      | bool   | Checks for updated AniBridge data. Default `true`.                     |
| `anime_mapping.provider`         | string | Dataset provider. Current value is `anibridge`.                        |
| `anime_mapping.release_tag`      | string | AniBridge release series. Default `v3`.                                |
| `anime_mapping.refresh_hours`    | int    | Minimum time between update checks. Default `24`. Minimum `1`.         |
| `anime_mapping.stale_after_days` | int    | Age where the UI marks the dataset stale. Default `14`. Minimum `1`.   |
| `anime_mapping.use_for_pairs`    | array  | Providers that activate mapping when present. Default `["anilist"]`.   |
| `anime_mapping.features`         | array  | Features where enrichment may run. Default `["watchlist", "ratings"]`. |

When enabled for the first time, CW downloads `stats.json` and `mappings.min.json`.

It then builds a local SQLite index.

Normal lookups use that local SQLite index.

CW does not call AniBridge for every synced item.

Related: [Meta: Anime ID Mapping](/crosswatch/settings/connections/metadata/anime-id-mapping.md).

### Scrobble (`scrobble`)

| Key                          | Type   | Notes                                                            |
| ---------------------------- | ------ | ---------------------------------------------------------------- |
| `scrobble.enabled`           | bool   | Master toggle.                                                   |
| `scrobble.mode`              | string | Legacy primary mode. `watch` or `webhook`.                       |
| `scrobble.sources.watcher`   | bool   | Enable Watcher ingestion.                                        |
| `scrobble.sources.webhook`   | bool   | Enable webhook ingestion.                                        |
| `scrobble.delete_plex`       | bool   | Legacy name but still valid. Auto-remove movies from watchlists. |
| `scrobble.delete_plex_types` | array  | Legacy name but still valid. Types: `movie`, `show`, `episode`.  |

`scrobble.sources` is the newer way to control Watcher and Webhooks independently.

If both are enabled, use filters so the same Plex server does not hit both paths.

#### Watcher mode (`scrobble.watch`)

| Key                                            | Type   | Notes                                          |
| ---------------------------------------------- | ------ | ---------------------------------------------- |
| `scrobble.watch.autostart`                     | bool   | Start watcher on boot if enabled + mode=watch. |
| `scrobble.watch.routes`                        | array  | Routes table (recommended).                    |
| `scrobble.watch.provider`                      | string | Legacy fallback. Prefer routes.                |
| `scrobble.watch.sink`                          | string | Legacy fallback. Prefer routes.                |
| `scrobble.watch.plex_simkl_ratings`            | bool   | Forward Plex ratings to SIMKL.                 |
| `scrobble.watch.plex_trakt_ratings`            | bool   | Forward Plex ratings to Trakt.                 |
| `scrobble.watch.plex_mdblist_ratings`          | bool   | Forward Plex ratings to MDBList.               |
| `scrobble.watch.pause_debounce_seconds`        | int    | Ignore micro-pauses just after start.          |
| `scrobble.watch.suppress_start_at`             | int    | Kill near-end start flaps (credits).           |
| `scrobble.watch.ignore_live_tv_dvr`            | bool   | Ignore Live TV / DVR style Plex events.        |
| `scrobble.watch.filters.username_whitelist`    | array  | `["name", "id:123", "uuid:abcd…"]`             |
| `scrobble.watch.filters.server_uuid`           | string | Legacy single-server filter for Plex.          |
| `scrobble.watch.filters.server_uuid_whitelist` | array  | Allow only these Plex server UUIDs.            |
| `scrobble.watch.filters.server_uuid_blacklist` | array  | Block these Plex server UUIDs.                 |

Route shape (each entry in `scrobble.watch.routes[]`):

As of **v0.9.15**, configure routes manually.

Legacy Watcher fields are not auto-converted anymore.

```json
{
  "id": "R1",
  "enabled": true,
  "provider": "plex",
  "provider_instance": "default",
  "sink": "trakt",
  "sink_instance": "TRAKT-P01",
  "filters": {}
}
```

#### Webhook mode (`scrobble.webhook`)

| Key                                                   | Type   | Notes                                                      |
| ----------------------------------------------------- | ------ | ---------------------------------------------------------- |
| `scrobble.webhook.pause_debounce_seconds`             | int    | Ignore micro-pauses.                                       |
| `scrobble.webhook.suppress_start_at`                  | int    | Suppress near-end start flaps.                             |
| `scrobble.webhook.suppress_autoplay_seconds`          | int    | Plex autoplay suppression window.                          |
| `scrobble.webhook.probe_session_progress`             | bool   | Probe Plex sessions to match item by ratingKey/sessionKey. |
| `scrobble.webhook.plex_trakt_ratings`                 | bool   | Forward Plex ratings to Trakt in webhook mode.             |
| `scrobble.webhook.filters_plex.username_whitelist`    | array  | Accepted Account.title values. Empty = allow all.          |
| `scrobble.webhook.filters_plex.server_uuid`           | string | Legacy single-server filter for Plex.                      |
| `scrobble.webhook.filters_plex.server_uuid_whitelist` | array  | Allow only these Plex server UUIDs.                        |
| `scrobble.webhook.filters_plex.server_uuid_blacklist` | array  | Block these Plex server UUIDs.                             |

#### Progress rules (`scrobble.trakt`)

Used by Trakt, SIMKL, and MDBList sinks.

| Key                                        | Type | Notes                          |
| ------------------------------------------ | ---- | ------------------------------ |
| `scrobble.trakt.progress_step`             | int  | Send progress in % steps.      |
| `scrobble.trakt.stop_pause_threshold`      | int  | STOP below this becomes PAUSE. |
| `scrobble.trakt.force_stop_at`             | int  | STOP at/above this is forced.  |
| `scrobble.trakt.regress_tolerance_percent` | int  | Clamp small backwards jumps.   |

### Scheduling (`scheduling`)

| Key                                  | Type   | Notes                                                                   |
| ------------------------------------ | ------ | ----------------------------------------------------------------------- |
| `scheduling.enabled`                 | bool   | Master toggle for standard scheduling.                                  |
| `scheduling.mode`                    | string | `disabled`, `hourly`, `every_n_hours`, `daily_time`, `custom_interval`. |
| `scheduling.every_n_hours`           | int    | Used by `every_n_hours`. Values below `1` become `1`.                   |
| `scheduling.daily_time`              | string | Used by `daily_time`, in `HH:MM` 24-hour format.                        |
| `scheduling.custom_interval_minutes` | int    | Used by `custom_interval`. Minimum `15`.                                |
| `scheduling.advanced`                | object | Advanced ordered job configuration.                                     |

If `every_n_hours` is `1`, the mode changes to `hourly`.

#### Advanced scheduling (`scheduling.advanced`)

Use this only if you want strict control.

It runs jobs in order.

* `scheduling.advanced.enabled` (bool)
* `scheduling.advanced.jobs[]` (array)

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

### UI (`ui`)

| Key                              | Type   | Notes                                                       |
| -------------------------------- | ------ | ----------------------------------------------------------- |
| `ui.theme`                       | string | `flat-dark`, `flat-light`, or `original`.                   |
| `ui.show_watchlist_preview`      | bool   | Shows the Watchlist widget on the **Main** page.            |
| `ui.show_recent_history_widget`  | bool   | Shows the Recent History widget on the **Main** page.       |
| `ui.show_latest_ratings_widget`  | bool   | Shows the Latest Ratings widget on the **Main** page.       |
| `ui.show_recent_scrobble_widget` | bool   | Shows the Recent Scrobble widget on the **Main** page.      |
| `ui.show_playingcard`            | bool   | Shows the Now Playing card.                                 |
| `ui.show_recent_activity`        | bool   | Shows the text-based Recent Activity list.                  |
| `ui.recent_activity_display`     | string | Controls Recent Activity by count or age.                   |
| `ui.recent_activity_limit`       | int    | Legacy or derived row limit. Normalized to `3` through `5`. |
| `ui.recent_syncs_display`        | string | Controls Recent Syncs by count or age.                      |
| `ui.recent_syncs_limit`          | int    | Legacy or derived row limit. Normalized to `3` through `5`. |
| `ui.show_quick_add_desktop`      | bool   | Shows Quick Add on desktop.                                 |
| `ui.show_quick_add_mobile`       | bool   | Shows Quick Add on mobile.                                  |
| `ui.protocol`                    | string | `http` or `https`.                                          |

#### TLS (`ui.tls`)

| Key                  | Type   | Notes                                                 |
| -------------------- | ------ | ----------------------------------------------------- |
| `ui.tls.self_signed` | bool   | Auto-generate a self-signed certificate when missing. |
| `ui.tls.hostname`    | string | Used for certificate CN/SAN.                          |
| `ui.tls.valid_days`  | int    | Certificate validity (days).                          |
| `ui.tls.cert_file`   | string | Optional override path to a PEM cert.                 |
| `ui.tls.key_file`    | string | Optional override path to a PEM key.                  |

### UI auth (`app_auth`)

CW UI authentication is mandatory.

| Key                                 | Type   | Notes                                                                    |
| ----------------------------------- | ------ | ------------------------------------------------------------------------ |
| `app_auth.enabled`                  | bool   | Retained for compatibility, but normalized to `true`.                    |
| `app_auth.username`                 | string | Login username.                                                          |
| `app_auth.reset_required`           | bool   | Forces credential setup and clears existing sessions.                    |
| `app_auth.remember_session_enabled` | bool   | Allows persistent login sessions.                                        |
| `app_auth.remember_session_days`    | int    | Normalized to `1` through `365`.                                         |
| `app_auth.password`                 | object | Password hashing config and stored hash.                                 |
| `app_auth.session`                  | object | Legacy single-session state.                                             |
| `app_auth.sessions`                 | array  | Current login sessions.                                                  |
| `app_auth.last_login_at`            | int    | Most recent successful login time.                                       |
| `app_auth.plex_sso`                 | object | Plex single sign-on link state. Added during normalization when missing. |

### Pairs (`pairs[]`)

Each entry is one sync connection.

| Field             | Type   | Notes                                        |
| ----------------- | ------ | -------------------------------------------- |
| `id`              | string | Generated by UI/API.                         |
| `enabled`         | bool   | Master switch.                               |
| `source`          | string | Provider name.                               |
| `source_instance` | string | Provider profile id (defaults to `default`). |
| `target`          | string | Provider name.                               |
| `target_instance` | string | Provider profile id (defaults to `default`). |
| `mode`            | string | `one-way` or `two-way`.                      |
| `features`        | object | Per-feature rules.                           |

#### Feature format

| Field    | Type | Meaning                  |
| -------- | ---- | ------------------------ |
| `enable` | bool | Run feature at all.      |
| `add`    | bool | Allow adds to target.    |
| `remove` | bool | Allow removes on target. |

Do not use `true` or `false` as the full feature value.

Example:

```json
{
  "pairs": [
    {
      "id": "pair_abc123",
      "enabled": true,
      "source": "PLEX",
      "source_instance": "default",
      "target": "SIMKL",
      "target_instance": "default",
      "mode": "two-way",
      "features": {
        "watchlist": { "enable": true, "add": true, "remove": false },
        "history":   { "enable": true, "add": true, "remove": false },
        "ratings":   { "enable": true, "add": true, "remove": false, "types": ["movies","shows"], "mode": "only_new", "from_date": "" }
      }
    }
  ]
}
```

#### Special: `pairs[].features.ratings`

| Key         | Type   | Notes                                                    |
| ----------- | ------ | -------------------------------------------------------- |
| `types`     | array  | `movies`, `shows`, `seasons`, `episodes`. `all` expands. |
| `mode`      | string | `only_new`, `from_date`, or `all`.                       |
| `from_date` | string | Used only when `mode = "from_date"`.                     |

Empty or invalid `types` values fall back to `movies` and `shows`.

CW clears `from_date` when `mode` is not `from_date`.

#### Special: AniList pair options

Pairs containing AniList can enable `use_anime_mapping` on Watchlist and Ratings.

They can also enable `anime_only_sync`.

`anime_only_sync` requires `use_anime_mapping`.

When enabled, AniList accepts only items with a usable AniList or MyAnimeList ID.

Items that would require AniList title search are skipped.

More detail: [AniList pair options](/crosswatch/settings/configure-pairs/anilist-pair-options.md).


---

# 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/crosswatch/advanced-and-api/configuration-reference.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.
