Configuration reference
File location, load rules, normalization, and key reference for config.json.
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.
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.
CW obtains the encryption key from one of these locations, in priority order:
CW_CONFIG_KEYCROSSWATCH_CONFIG_KEY<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 for the complete key management and recovery process.
Avoid editing config.json by hand.
Most settings are configurable in the UI.
Use this page as a reference, not as a setup checklist.
Related config docs
At a glance
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.
1
CONFIG_BASE env var is set
CONFIG_BASE value
2
Running in Docker image (/app exists)
/config
3
Running from source
Project root (one level above cw_platform/)
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:
api.versionAPI.CURRENT_VERSIONAPP_VERSIONFallback
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:
API keys
Access and refresh tokens
Client secrets
Plex tokens and home PINs
Session IDs and token hashes
Password hashes and salts
Device and request tokens
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:
security.webhook_ids.plextraktsecurity.webhook_ids.jellyfintraktsecurity.webhook_ids.embytraktsecurity.webhook_ids.plexwatcher
Generated values use token_urlsafe(24).
Scheduling
scheduling.mode is normalized to one of:
disabledhourlyevery_n_hoursdaily_timecustom_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:
PublicMetaDB
PublicMetaDB page sizes, page limits, contribution limits, and request limits are clamped to supported ranges.
The default request limits are:
User interface
ui.theme is normalized to:
flat-darkflat-lightoriginal
Recent Activity and Recent Sync display values support:
count:3count:4count:5hours:24hours:48hours: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:
When a feature object omits these fields, CW uses:
A non-object feature value is treated as invalid and normalized to:
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:
only_newfrom_dateall
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:
More detail: AniList pair options.
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:
Plex
Trakt
SIMKL
AniList
MDBList
PublicMetaDB
Tautulli
Jellyfin
Emby
TMDb
TMDb Sync
The following local authentication values are also redacted:
app_auth.password.hashapp_auth.password.saltapp_auth.session.token_hashToken hashes inside
app_auth.sessions[]All values under
security.webhook_ids
Key reference
Top-level
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.
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)
security)This node stores webhook route IDs.
CW can auto-generate them.
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.
If you change these IDs, update your webhook URLs.
Restart CW after edits.
Providers
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
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
Plex
Connection
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
plex.scrobble.libraries
array
Shared by Plex Watcher and Plex Webhooks. Empty means all libraries.
History
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
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)
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).
TMDb (Sync)
Used only when you connect TMDb (Sync) for watchlist/ratings syncing.
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.
Trakt
OAuth
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
trakt.timeout
int
HTTP timeout (seconds).
trakt.max_retries
int
Retry budget (429/5xx backoff).
Rate limiting
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
Watchlist
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
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
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.
SIMKL
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
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
AniList
Authentication
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.
MDBList
Authentication
MDBList supports Device Code authentication and legacy API key authentication.
New connections should use Device Code authentication.
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
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
Watchlist
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
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
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.
PublicMetaDB
Connection
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
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
publicmetadb.history_per_page
int
Normalized to 1 through 500.
publicmetadb.history_max_pages
int
Normalized to 1 through 100000.
Progress
publicmetadb.progress_per_page
int
Normalized to 1 through 500.
publicmetadb.progress_max_pages
int
Normalized to 1 through 100000.
Ratings
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
publicmetadb.rate_limit.post_per_sec
number
Normalized to 0 through 3.
publicmetadb.rate_limit.get_per_sec
number
Normalized to 0 through 20.