Settings
Application settings and configuration
GET /settings/providers
Section titled “GET /settings/providers”List provider configuration status
Returns encryption readiness and per-provider status (configured, source, and optional baseUrl/model). Never returns key values.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Provider status summary | object |
200 response body
Section titled “200 response body”cryptoReady(boolean)anthropic(ProviderStatus)openai(ProviderStatus)openrouter(ProviderStatus)whisper(ProviderStatus)
PUT /settings/providers/{provider}
Section titled “PUT /settings/providers/{provider}”Update provider configuration
Parameters
Section titled “Parameters”provider(path, string, one ofanthropic,openai,openrouter,whisper,ollama,typesafe,systemone-compatible, required)
Request body (application/json, required)
Section titled “Request body (application/json, required)”apiKey(string or null): Omit to leave unchanged; null or empty to clearbaseUrl(string)model(string)affectedRunsAction(string, one ofrequeue,cancel): What to do with runs frozen against the old account when this save moves the account identity. Ignored when the identity is unchanged (a same-account key rotation). Defaultrequeue.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated status. Carries accountChanged and affectedRuns when the save moved the slot’s account identity. | ProviderStatus |
| 400 | Invalid input (e.g. | |
| 404 | Unknown provider | |
| 409 | provider_crypto_unavailable (MINUSPOD_MASTER_PASSPHRASE unset) |
DELETE /settings/providers/{provider}
Section titled “DELETE /settings/providers/{provider}”Clear stored provider key
Removes the DB-stored key so env-var fallback (if any) takes over.
Parameters
Section titled “Parameters”provider(path, string, one ofanthropic,openai,openrouter,whisper,ollama,typesafe,systemone-compatible, required)
Request body (application/json)
Section titled “Request body (application/json)”affectedRunsAction(string, one ofrequeue,cancel): What to do with runs frozen against the cleared endpoint’s account. Clearing a base URL moves the account identity. Defaultrequeue.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Cleared. Carries accountChanged and affectedRuns when clearing the endpoint moved the slot’s account identity. | ProviderStatus |
| 400 | Unknown affectedRunsAction | |
| 404 | Unknown provider |
GET /settings/providers/{slot}/affected-runs
Section titled “GET /settings/providers/{slot}/affected-runs”Runs a change to this account slot would strand
Preflight for a provider or endpoint change: lists the active runs whose frozen routes still name the slot’s current account, so the UI can show what a save is about to interrupt before writing anything. A same-account key rotation strands nothing and needs no preflight.
Parameters
Section titled “Parameters”slot(path, string, one ofprimary,secondary,a,b, required):a/bare accepted as aliases ofprimary/secondary.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | The slot’s current account and the runs bound to it | object |
| 404 | Unknown provider slot |
200 response body
Section titled “200 response body”slot(string, one ofprimary,secondary)accountId(string or null): Non-secret account identity (provider type plus endpoint); null when the slot has no provider configuredaffectedRuns(AffectedRuns)
POST /settings/providers/rotate-passphrase
Section titled “POST /settings/providers/rotate-passphrase”Report that master-passphrase rotation must run offline
Online rotation is refused because sibling workers cannot safely
receive the replacement passphrase. Stop all workers and run
python scripts/rotate_master_passphrase.py, update the deployment
environment, then restart every worker together.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 409 | Rotation requires the offline command |
POST /settings/providers/{provider}/test
Section titled “POST /settings/providers/{provider}/test”Test a provider connection
Native System One providers check model discovery without a stage model. Success confirms the catalog connection, not inference.
Parameters
Section titled “Parameters”provider(path, string, one ofanthropic,openai,openrouter,whisper,ollama,typesafe,systemone-compatible, required)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Test result (always 200; see ok field) |
object |
200 response body
Section titled “200 response body”ok(boolean)error(string)detail(string)reachable(boolean)status(integer)validation(string, one ofmodel_catalog)inferenceChecked(boolean): False for native model-catalog checks
POST /settings/providers/{provider}/test-connection
Section titled “POST /settings/providers/{provider}/test-connection”Test a configured endpoint connection
Connection probe for providers with a configurable endpoint.
whisper uploads a one-second generated audio sample through the
same request shape and upload format (FLAC by default, WAV when
Skip FLAC compression is on) as the real transcription path, so the
result reflects what an actual episode upload would hit, including
the wrong-path case (OpenVINO Model Server only answers under its
versioned base such as /v3). openai and ollama hit the same
/models route the real LLM client uses for discovery, with the
same Ollama /v1 normalization, and unlike /test need no stored
key. Accepts unsaved values so a URL can be tested before saving; a
baseUrl key present in the body is authoritative even when empty.
The stored API key is sent only when the tested URL points at the
same scheme/host/port as the saved base URL, so the key cannot be
sent to an arbitrary URL. anthropic and openrouter probe their
fixed public endpoints with the saved key; the request body is
ignored for them, distinguishing an unreachable network from a
rejected or missing key.
typesafe and systemone-compatible check their /models endpoint
without a stage model. Success confirms model discovery, not inference;
public catalogs may not validate keys. TypeSafe requires its own saved
key. Compatible endpoints can omit a key; a saved key is sent only to
the same saved scheme, host and port. Native probes use
the bounded connection-probe timeout.
Parameters
Section titled “Parameters”provider(path, string, one ofwhisper,openai,ollama,anthropic,openrouter,typesafe,systemone-compatible, required)
Request body (application/json)
Section titled “Request body (application/json)”baseUrl(string): Base URL to test; when the key is absent the saved value is usedmodel(string): Transcription model to test (faster-whisper or remote API); ignored for native System One catalog checksskipFlacCompression(boolean): whisper only: upload WAV (true) or FLAC (false); falls back to the saved setting
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Probe result (always 200; see ok field) |
ConnectionTestResult |
| 400 | Invalid input (non-string baseUrl or model, non-boolean skipFlacCompression) | |
| 404 | Provider without a testable endpoint |
POST /settings/providers/secondary/test-connection
Section titled “POST /settings/providers/secondary/test-connection”Test the secondary provider connection
Same staged probe as /settings/providers/{provider}/test-connection,
but targets the optional secondary provider slot: its key comes from
secondaryProviderApiKey, and its type and endpoint come from
secondaryProvider/secondaryProviderBaseUrl unless the request
body’s provider/baseUrl override them, to test an unsaved form
value before Save. Returns ok: false with a detail explaining the
reason when no secondary provider type is configured yet.
Request body (application/json)
Section titled “Request body (application/json)”provider(string, one ofanthropic,openrouter,openai-compatible,ollama,typesafe,systemone-compatible): Provider type to test; when absent the saved secondaryProvider is usedbaseUrl(string): Base URL to test; when absent the saved secondary value is used
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Probe result (always 200; see ok field) |
ConnectionTestResult |
| 400 | Invalid input (non-string baseUrl, non-string provider, or provider not one of the supported types) |
POST /settings/providers/failover/test-connection
Section titled “POST /settings/providers/failover/test-connection”Test the LLM failover provider connection end to end
Same staged probe as /settings/providers/secondary/test-connection,
but targets the shared LLM failover slot (#806): its key comes from
failoverLlmApiKey, and its type and endpoint come from
failoverLlmProvider/failoverLlmBaseUrl unless the request body’s
provider/baseUrl override them, to test an unsaved form value
before Save. Returns ok: false with a detail explaining the
reason when no failover provider type is configured yet.
Request body (application/json)
Section titled “Request body (application/json)”provider(string, one ofanthropic,openrouter,openai-compatible,ollama,typesafe,systemone-compatible): Provider type to test; when absent the saved failoverLlmProvider is usedbaseUrl(string): Base URL to test; when absent the saved failover value is used
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Probe result (always 200; see ok field) |
ConnectionTestResult |
| 400 | Invalid input (non-string baseUrl, non-string provider, or provider not one of the supported types) |
POST /settings/providers/failover-whisper/test-connection
Section titled “POST /settings/providers/failover-whisper/test-connection”Test the transcriber failover connection end to end
Same staged probe as /settings/providers/whisper/test-connection
(#806), but targets the transcriber failover slot: its key comes
from failoverWhisperApiKey, and its base URL/model come from
failoverWhisperApiBaseUrl/failoverWhisperApiModel unless the
request body overrides them.
Request body (application/json)
Section titled “Request body (application/json)”baseUrl(string): Base URL to test; when the key is absent the saved value is usedmodel(string): Model name to send; falls back to the saved valueskipFlacCompression(boolean): Upload WAV (true) or FLAC (false); falls back to the saved setting
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Probe result (always 200; see ok field) |
ConnectionTestResult |
| 400 | Invalid input (non-string baseUrl or model, non-boolean skipFlacCompression) |
POST /settings/podcast-index/test
Section titled “POST /settings/podcast-index/test”Test the saved PodcastIndex credentials
Sends the same signed request the podcast search uses (a one-result search), so a passing test means search will work. Uses only the saved credentials; unsaved form drafts must be saved first.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Probe result (always 200; see ok field) |
ConnectionTestResult |
POST /settings/feed-auth/regenerate-key
Section titled “POST /settings/feed-auth/regenerate-key”Rotate the global feed auth key
Generates a new global feed auth key, replacing the current one. The old key is rejected immediately, so podcast apps subscribed with the previous key must be re-subscribed using the new URLs.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Key rotated | object |
| 409 | feed_auth_disabled (enable feed auth before rotating the key) |
200 response body
Section titled “200 response body”feedAuthKey(string): New global feed auth key (64 hex chars)
GET /settings
Section titled “GET /settings”Get all settings
Returns current application settings including ad detection configuration
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Current settings | Settings |
PUT /settings/ad-detection
Section titled “PUT /settings/ad-detection”Update ad detection settings
Update AI model, system prompt, or multi-pass detection
Request body (application/json, required)
Section titled “Request body (application/json, required)”systemPrompt(string): System prompt for AI ad detection (first pass)verificationPrompt(string): System prompt for verification pass (post-cut re-detection of missed ads)chapterPrompt(string): Prompt template for chapter topic detection with placeholders {num_splits}, {segment_start}, {segment_end}, {continuation_block}, {description_block}, {hints_block}, {transcript}chapterPromptOverride(string): Optional override appended to the chapter topic-detection prompt (empty by default)patternCleanupPrompt(string): System prompt for the pattern cleanup review. Blank resets it to the default.claudeModel(string): AI model ID to use for first pass ad detection. No model ships by default; unset until configured.verificationModel(string): AI model ID for verification pass (can differ from first pass). No model ships by default; unset until configured.chaptersModel(string): Model for chapter title generation and topic detection (smaller/cheaper models work well). No model ships by default; unset until configured.detectionProvider(string, one ofprimary,secondary, ``): Routing slot for first-pass ad detection:primaryruns it on the global llmProvider,secondaryon the optional secondary provider (falls back to primary if secondary is disabled or unconfigured). Empty string clears the override back to primary.verificationProvider(string, one ofprimary,secondary,same_as_detection, ``): Routing slot for the verification pass:primary,secondary, orsame_as_detectionto inherit detectionProvider’s resolved slot. Empty string clears the override back to same_as_detection.chaptersProvider(string, one ofprimary,secondary,same_as_detection, ``): Routing slot for chapter title generation:primary,secondary, orsame_as_detectionto inherit detectionProvider’s resolved slot. Empty string clears the override back to same_as_detection.whisperModel(string): faster-whisper model (tiny, base, small, medium, large-v3); ignored by the parakeet and openai-api backendswhisperBackend(string, one oflocal,openai-api,parakeet): Whisper backend to use (local uses local GPU/CPU, openai-api uses an external OpenAI-compatible API, parakeet runs NVIDIA Parakeet TDT 0.6B v3 in-process)whisperApiBaseUrl(string): Base URL for OpenAI-compatible remote transcription API endpoint (only used when whisperBackend is openai-api)whisperApiKey(string): API key for the remote transcription API endpoint (only used when whisperBackend is openai-api)whisperApiModel(string): Model name to use with the remote transcription API (only used when whisperBackend is openai-api)whisperLanguage(string): ISO 639-1 language code to pin transcription to (e.g.en,fi,es), orautoto let the backend detect. Empty value resets to the default (en). Applies to both local (faster-whisper) and remote OpenAI-compatible backends.whisperApiTimeoutSeconds(integer): Seconds to wait for each transcription request to the remote transcription backend (issue #593). A slow self-hosted backend that returns 0 segments on larger chunks is hitting this limit; raise it or lower transcribeMaxChunkSeconds. A proxy in front of the backend may cut the request sooner than this value.transcribeMaxChunkSeconds(integer): Max duration, in seconds, of each transcription chunk.transcribeConcurrentChunks(integer): Number of transcription chunks sent to the transcription backend concurrently.transcribeChunkOverlapSeconds(integer): Overlap, in seconds, between consecutive transcription chunks.whisperPoolEnabled(boolean): Bound concurrent requests to the remote transcription API to avoid rate limits.whisperPoolMaxRequests(integer): Maximum concurrent in-flight requests to the remote transcription API when the pool is enabled.whisperPoolMaxEpisodes(integer): Maximum episodes transcribing concurrently against the remote transcription API when the pool is enabled.whisperComputeType(string, one ofauto,float16,int8_float16,int8,float32): Compute precision for the local faster-whisper backend only.autoresolves to float16 on CUDA and int8 on CPU. On init failure with float16, the server falls back through int8_float16 -> int8 -> float32. Older NVIDIA GPUs (Pascal GTX 10xx, Maxwell GTX 9xx) needint8orfloat32. See the Transcription guide (docs/transcription.md, GPU compute type) for the per-GPU table.audioCueDetectionEnabled(boolean): Master toggle for audio cue detection (#350). Off by default.audioCueFreqMinHz(number): Low edge (Hz) of the spectral fallback cue band.audioCueFreqMaxHz(number): High edge (Hz) of the spectral fallback cue band. Must exceed the low edge.audioCueProminenceDb(number): dB above the in-band baseline for a burst to count (spectral fallback).audioCueMinConfidence(number): Drop cues weaker than this.audioCueTemplateScore(number): ZNCC score a learned cue template must reach on another episode (default 0.75).audioCueFormantAttenDb(number): Voiceover-robust matching (#350). Attenuates the 800-3400 Hz speech-formant band in dB during cue-template matching so a cue keys on its constant music bed despite a varying voiceover. 0 disables it (default); existing/ding/full-spectrum cues are unchanged. A per-template override may supersede this. Typical opt-in ~9-12.audioCueSnapConfidence(number): Min cue confidence before a cue may move an ad edge (default 0.80).audioCueSnapLeadSeconds(number): How far back from the ad edge a cue may sit to be eligible for snapping (default 10.0s).audioCueSnapLagSeconds(number): How far past the ad edge a cue may sit to be eligible for snapping (default 4.0s).audioCueCaptureMinSeconds(number): Shortest cue a user may bracket (default 0.2s).audioCueCaptureMaxSeconds(number): Longest cue a user may bracket (default 4s).audioCueCaptureMaxIntroSeconds(number): Longest show-intro stinger a user may bracket (default 60s).audioCueCaptureMaxOutroSeconds(number): Longest show-outro stinger a user may bracket (default 60s).audioCuePairConfidence(number): Min cue confidence to synthesize an ad from a cue pair (default 0.85).audioCuePairMinBreakSeconds(number): Shortest cue-pair break (default 30s).audioCuePairMaxBreakSeconds(number): Longest cue-pair break (default 480s).audioCuePairMaxBreakFraction(number): Reject a cue pair spanning more than this fraction of the episode (short-episode backstop; default 0.5, 0 disables).audioCueCreateFromPairs(boolean): Opt-in cue-pair ad synthesis. Off by default.audioCueCutMatches(boolean): Cut every matched cue on its own, with no ad next to it. Templates and feeds can override. Off by default.silenceSnapNoiseDb(number): Amplitude (dBFS) below which audio counts as silence for silence snap (default -50).silenceSnapMinDurationSeconds(number): Shortest sub-threshold span that counts as a silence for silence snap (default 0.3s).silenceSnapMaxDistanceSeconds(number): Farthest an ad edge may move to reach a detected silence (default 2.0s).positionalPriorEnabled(boolean): Toggles the learned positional prior experiment (issue 360). When on, feeds with at least 5 processed episodes get per-feed learned ad-break zones, used as a scrutiny hint in the detection prompt and as position-based confidence boosts during validation. Off by default.vadGapDetectionEnabled(boolean): Toggles the VAD gap detector, which cuts audio regions Whisper’s VAD dropped (sped-up disclaimers, distorted ad tails). Advanced tuning; intentionally not surfaced in the UI. See the Configuration guide (docs/configuration.md, VAD Gap Detector) for full behavior.vadGapStartMinSeconds(number (float)): Minimum pre-transcript head gap, in seconds, that the VAD detector will cut. Values below the threshold are left alone.vadGapMidMinSeconds(number (float)): Minimum mid-episode untranscribed gap for the standalone emission path. Gaps adjacent to an already-detected ad extend that ad in place regardless of this threshold.vadGapTailMinSeconds(number (float)): Minimum post-transcript tail gap that the VAD detector will cut when no postroll marker already covers it.minContentBetweenAdsSeconds(number (float)): Minimum seconds of actual speech content that must appear in the gap between two ads to keep them separate. Gaps with less content (ad-transition music/silence) are collapsed into one contiguous cut. Set to 0 to disable. Default 12.0.adDetectionExcludeStartSeconds(number (float)): Seconds from the start of an episode during which ad markers are ignored, so an intro cue is not mistaken for an ad break. 0 disables it. Feeds inherit it or set adDetectionExcludeStartOverride.maxAdDurationSeconds(number (float)): Length past which an ad must name a sponsor MinusPod recognizes to be cut. One that does not is held for review rather than dropped.maxAdDurationConfirmedSeconds(number (float)): Hard ceiling. Even an ad with a confirmed sponsor is held rather than cut past this length.maxLlmWindowAdSeconds(number (float)): Longest single ad the model may report within one detection window; a longer one is discarded. For each feed the cap is the larger of this value, the feed’s maxAdDurationOverride and the feed’s maxAdDurationRejectOverride. The verification pass uses the same per-feed cap. Out-of-range stored values are clamped to 60 to 900 on read.autoProcessEnabled(boolean): Enable automatic processing of new episodes when feeds refreshautoProcessMaxAgeHours(integer): Only episodes published within this many hours are auto-queued (default 48). Feeds can override it.maxFeedEpisodes(integer): Global default cap on episodes returned in served RSS feeds. Overridden by per-feed maxEpisodes when set.onlyExposeProcessedDefault(boolean): Global default for hiding upstream episodes whose status is not ‘processed’ from served RSS feeds. Overridden by per-feed onlyExposeProcessedEpisodes when set.feedItunesBlock(boolean): Add itunes:block Yes to every served feed so app directories skip it and Pocket Casts stops matching it to the public show. Default true.requireManualApprovalDefault(boolean): Global default for holding newly processed episodes out of served feeds until approved. Overridden by per-feed requireManualApproval when set.whatsNewOnUpgrade(boolean): Show the What’s new dialog once per browser after the app version changes, or on a browser’s first load.detectShowSegments(boolean): Global default for detecting intro, outro, and recap/housekeeping segments in addition to the always-detected ad-side categories. Overridden by per-feed detectShowSegments when set.seedSponsorsDetection(boolean): Whether the detection pass prompt is rendered with the seed sponsor list. Off renders that prompt with an empty sponsor block. Default true.seedSponsorsVerification(boolean): Whether the verification pass prompt is rendered with the seed sponsor list. Off renders that prompt with an empty sponsor block. Default true.seedSponsorsReviewer(boolean): Whether the reviewer pass prompt is rendered with the seed sponsor list. Off renders that prompt with an empty sponsor block. Default true.seedSponsorsResurrect(boolean): Whether the resurrect pass prompt is rendered with the seed sponsor list. Off renders that prompt with an empty sponsor block. Default true.textRecurrenceHints(boolean): Hints the detection prompt with transcript spans whose wording recurs near-verbatim across prior episodes of the same show (intros, disclaimers, credits). Off by default.adAddressingMode(string, one oftimestamps,segment_ids,random): How the LLM addresses detected segments. ‘timestamps’ (default) reports absolute start/end seconds. ‘segment_ids’ reports transcript line ids instead, which the server maps to exact segment times. ‘random’ draws one of the two per detection run and records which, for the /stats/addressing compliance comparison.processNewEpisodesFirst(boolean): Whether episodes published within the last 48 hours get an automatic queue-priority boost ahead of the rest of the auto-process backlog. On by default.rssRefreshIntervalMinutes(integer): Minutes between background RSS refresh passes for every feed. A change applies after the wait already in progress finishes. Default 15.queueManualBoost(integer): Priority added when a user plays an unprocessed episode or reprocesses one by hand. Default 20.queueFreshBoost(integer): Priority added to episodes published within the last 48 hours while processNewEpisodesFirst is on. Default 5.queueBulkBoost(integer): Priority added to bulk work (Reprocess All, segment re-renders). Default 0 keeps backlog runs behind everything else. Default 0.podpingEnabled(boolean): Opt-in listener that polls public Hive API nodes for Podping publish notifications and refreshes a matching feed immediately. Off by default.segmentCategoryActions(map of string, one ofremove,beep,keep,mark): Partial global segment-category action map (issuecommunitySyncCategories(array of string, one ofsponsor,cross_promo,self_promo,interaction,intro,outro,recap): Segment categories accepted from community pattern sync. Default is every category. Replaces the stored list outright.jitBlockedUserAgents(array of string): User-agent patterns barred from triggering just-in-time episode processing. Case-insensitive substring match; a leading caret anchors to the start of the agent.artworkWatermarkEnabled(boolean): When true, the served feed’s channel cover art gets a MinusPod badge overlaid so the filtered feed is distinct in a podcast app (issueartworkBadgePosition(string, one ofbottom-right,bottom-left,top-right,top-left): Corner the MinusPod badge is composited into (issuelowAdYieldAction(string, one ofnothing,redetect,reprocess,full): Automatic response when a pipeline-initiated run removes far less ad time than the feed’s recent average: ‘nothing’ (default), ‘redetect’ (LLM-only rerun from the stored transcript), ‘reprocess’, or ‘full’. Fires at most once per episode.episodeLogRetentionDays(integer): Days to keep per-run pipeline logs (issue 660). 0 turns run log storage off and sweeps what is already stored.episodeLogLevel(string, one ofdebug,info): Minimum level kept in a run log (issue 660). Defaults to debug.downloadUserAgent(string): User-Agent sent when fetching audio, artwork, and chapters. Printable ASCII on a single line. Send an empty string to restore the default.feedUserAgent(string): User-Agent sent when fetching RSS feeds. Printable ASCII on a single line. Send an empty string to restore the default.logDownloadQuery(boolean): Include URL query strings in download logs. Off by default: an enclosure query string regularly carries a signed CDN token or a listener tracking id, and logs outlive both. The path and redirect chain are logged either way.feedAuthEnabled(boolean): Require the global feed auth key on all public feed and episode URLs. Enabling generates the key if absent.downloadHistoryEnabled(boolean): Record episode downloads for the History Downloads tab.downloadGeoLookupEnabled(boolean): Resolve public client IPs to a location through ipwho.is. Off by default.downloadHistoryRetentionDays(integer): Days to keep download history rows.minCutConfidence(number (float)): Minimum confidence threshold to cut an ad from audio. Lower = more aggressive (removes more potential ads), higher = more conservativevttTranscriptsEnabled(boolean): Generate a VTT transcript sidecar for each processed episode.chaptersEnabled(boolean): Generate podcast:chapters JSON for each processed episode.chaptersMode(string, one ofauto,generate,off): Default chapter mode for feeds that inherit this setting.skipSecondPass(boolean): When true, feeds that inherit this setting skip the pass-2 verification scan.transcriptDifferentialEnabled(boolean): Default for the upstream-transcript differential; feeds that inherit this setting follow it. Diffs the upstream podcast:transcript against the transcript to help locate ads.differentialFetchMode(string, one ofauto,on,off): Default cross-fetch mode for feeds that inherit this setting.chaptersInNotes(boolean): Append the generated chapter list to served episode descriptions and the episode API description. Feeds can override per feed.reviewLinksInNotes(boolean): Append review and rate links (episode page and a 5-star rating link) to the served description of processed episodes. Feeds can override per feed.adChaptersEnabled(boolean or string, one oftrue,false,on,off): Deprecated. Translates into segmentCategoryActions; false or ‘off’ demotes every mark to keep, true or ‘on’ is accepted and ignored.adChapterCategories(map of boolean): Deprecated. Translates into segmentCategoryActions; true promotes keep to mark, false demotes mark to keep; remove and beep stay unchanged.adChaptersIncludeHeld(boolean): Also publish held-for-review segments as ad chapters.adChapterTitleFormat(string): Title template for an ad chapter; {label} is the category name and {category} its id. Send an empty string to restore the default.adChapterHeldTitleFormat(string): Title template for a held segment’s ad chapter; {label} is the category name and {category} its id. Send an empty string to restore the default.adChapterResumeTitle(string): Title of the chapter that marks the return to show content after an ad chapter. Send an empty string to restore the default.adChapterMinConfidence(number (float)): Minimum marker confidence required before a segment becomes an ad chapter.audioReplacementSoundEnabled(boolean or null): Insert the replacement clip for Remove actions. Null resets to the default. Defaulttrue.audioMp3StreamCopyEnabled(boolean or null): Try compatible MP3 cuts; otherwise re-encode. Null resets to the default. Defaultfalse.audioBitrate(string, one of64k,96k,128k,192k,256k): Output bitrate used when re-encoding processed audio.audioEncoderCompressionLevel(string, one ofdefault,0,1,2,3,4,5,6,7,8,9): libmp3lame -compression_level: 0 is slowest/best, 9 is fastest. ‘default’ omits the flag, leaving ffmpeg’s own encoding behavior unchanged.skipFlacCompression(boolean): When true, skip the FLAC encode pass before uploading audio to the remote transcription API. Send the preprocessed WAV directly.audioNormalizeEnabled(boolean): Loudness-normalize processed audio output.audioNormalizeIntensity(string, one ofgentle,normal,aggressive,extreme,maximum): Loudness normalization intensity, applied when audioNormalizeEnabled is true.adDetectionParallelWindows(integer): Number of transcript windows the ad detector processes concurrently through the LLM.adReviewerParallelAds(integer): Number of ads the reviewer asks the LLM about at the same time.reviewProvider(string, one ofprimary,secondary,same_as_pass): Routing slot for the reviewer pass.same_as_pass(default) inherits both the provider and model of whichever pass is being reviewed.primaryorsecondaryruns the reviewer on that slot instead and enables reviewModel; secondary falls back to primary when it is disabled or unconfigured.maxArtworkBytes(integer): Cover art download size cap in bytes.maxRssBytes(integer): RSS response body size cap in bytes (no ceiling).maxAudioDownloadMb(integer): Per-episode audio download cap in MB (no ceiling).localUploadMaxMb(integer): Maximum size in MB of one local-feed upload request. Default1024.llmProvider(string, one ofanthropic,openai-compatible,ollama,openrouter,typesafe,systemone-compatible): LLM provider for detection and verification; System One review is experimental. Chapters and Pattern Cleanup require a chat provider when enabled.omitTemperature(boolean): Operator override: when true, omit the temperature parameter from every LLM call, for every model, regardless of the built-in list of models known to reject it or anything learned at runtime from a prior rejection. Use ahead of a newly-released model that rejects temperature, without waiting for a failed call or a code change. Off by default.llmJsonSchemaEnabled(boolean): Opt-in (#693): send response_format json_schema on detection, review, category-repair, and trim-recovery calls to OpenAI-compatible endpoints, for each model that passes the probe. Support is tracked per model, not per endpoint; unsupported models fall back to json_object. Off by default.typesafeApiKey(string or null): Primary TypeSafe key. Omit to preserve; null or empty clears the stored key and restores TYPESAFE_API_KEY fallback. Separate from chat-provider credentials.systemoneApiKey(string or null): Optional primary compatible key. Omit to preserve; null or empty clears the stored key and restores SYSTEMONE_API_KEY fallback.systemoneBaseUrl(string or null): Primary compatible API base URL; MinusPod appends /systemone. Empty or null clears the saved endpoint and restores SYSTEMONE_BASE_URL fallback. Default empty. TypeSafe always uses its canonical endpoint.systemOneTunables(SystemOneProfilesPatch)openaiBaseUrl(string): Base URL for OpenAI-compatible API endpoint (only used when provider is not anthropic)pricingSourceMode(string, one ofauto,litellm,free): Pricing source override. ‘auto’ picks by provider/domain; ‘litellm’ always uses the LiteLLM catalog; ‘free’ disables cost tracking (use only for local models with no cost).modelPricingOverrides(map of one of ModelPricingOverride, null): Partial map of model IDs to operator pricing in USD per 1 million tokens. Operator pricing takes precedence over catalog pricing. Set an entry to null to remove its override. Both rates may be 0 for a free model.openrouterApiKey(string): API key for OpenRouter (only used when provider is openrouter)secondaryProviderEnabled(boolean): Turn the optional secondary provider slot on or off. A per-stage provider override set to secondary while this is false falls back to primary. Off by default.secondaryProvider(string or null, one ofanthropic,openai-compatible,ollama,openrouter,typesafe,systemone-compatible, ``,null): Secondary provider type. Empty or null clears it.secondaryProviderBaseUrl(string): Base URL for the secondary provider, used when its type has a configurable endpoint (openai-compatible, ollama). SSRF-validated like every other operator-configured URL. Empty clears the override back to the default.secondaryProviderApiKey(string or null): API key for the secondary provider. Omit to leave unchanged; null or empty to clear.providerRequestsPerMin(integer): Manual requests-per-minute cap for the primary provider account. 0 means unlimited.providerRequestsPerDay(integer): Manual requests-per-day cap for the primary provider account. 0 means unlimited.secondaryProviderRequestsPerMin(integer): Manual requests-per-minute cap for the secondary provider account. 0 means unlimited.secondaryProviderRequestsPerDay(integer): Manual requests-per-day cap for the secondary provider account. 0 means unlimited.providerTokensPerMin(integer): Manual tokens-per-minute cap for the primary provider account. 0 means unlimited. Reservation-based, not recorded-usage-based: each dispatch reserves an estimate (prompt tokens plus max_tokens) before the request goes out and reconciles to actual usage when the call finishes, so a single large request cannot cross the cap unseen. The cost is that an over-estimated call holds more of the window than it spends until it completes.secondaryProviderTokensPerMin(integer): Manual tokens-per-minute cap for the secondary provider account. 0 means unlimited. Reservation-based in the same way as providerTokensPerMin: an estimate is reserved at dispatch and reconciled to actual usage on completion.providerBEnabled(boolean): Alias of secondaryProviderEnabled.providerB(string or null, one ofanthropic,openai-compatible,ollama,openrouter, ``,null): Alias of secondaryProvider.providerBBaseUrl(string): Alias of secondaryProviderBaseUrl.providerBApiKey(string or null): Alias of secondaryProviderApiKey.providerBRequestsPerMin(integer): Alias of secondaryProviderRequestsPerMin.providerBRequestsPerDay(integer): Alias of secondaryProviderRequestsPerDay.providerBTokensPerMin(integer): Alias of secondaryProviderTokensPerMin.providerATimeoutSeconds(one of number, null, constant ``): Request timeout for Provider A. Null or blank clears it to the provider-type default; omitted leaves it unchanged. Native providers accept positive finite fractional seconds; chat providers require integer seconds from 10 to 3600.providerAMaxRetries(one of integer, null, constant ``): Max retries for Provider A. Null or blank clears it to the provider-type default; omitted leaves it unchanged.providerBTimeoutSeconds(one of number, null, constant ``): Request timeout for Provider B. Null or blank clears it to the provider-type default; omitted leaves it unchanged. Native providers accept positive finite fractional seconds; chat providers require integer seconds from 10 to 3600.providerBMaxRetries(one of integer, null, constant ``): Max retries for Provider B. Null or blank clears it to the provider-type default; omitted leaves it unchanged.failoverLlmEnabled(boolean): Enable or disable the shared LLM failover provider. Off by default.failoverLlmProvider(string or null, one ofanthropic,openai-compatible,ollama,openrouter, ``,null): LLM failover provider type. Empty or null clears it.failoverLlmBaseUrl(string): Base URL for the LLM failover provider; requests are SSRF-validated.failoverLlmApiKey(string or null): API key for the LLM failover provider. Omit to leave unchanged; null or empty to clear.failoverLlmTimeoutSeconds(one of integer, null, constant ``): Request timeout for the LLM failover provider. Null or blank clears it to the provider-type default; omitted leaves it unchanged.failoverLlmMaxRetries(one of integer, null, constant ``): Max retries for the LLM failover provider. Null or blank clears it to the provider-type default; omitted leaves it unchanged.failoverLlmDetectionModel(string): Model the LLM failover provider uses for ad detection.failoverLlmReviewModel(string): Model the LLM failover provider uses for review. Blank inherits the detection model.failoverLlmVerificationModel(string): Model the LLM failover provider uses for verification. Blank inherits the detection model.failoverLlmChaptersModel(string): Model the LLM failover provider uses for chapters. Blank inherits the detection model.failoverWhisperEnabled(boolean): Enable or disable the transcriber failover provider. Off by default.failoverWhisperBackend(string, one oflocal,openai-api,parakeet): Transcriber failover backend (parakeet runs NVIDIA Parakeet TDT 0.6B v3 in-process).failoverWhisperModel(string): Local model name for the transcriber failover, when its backend is local.failoverWhisperApiBaseUrl(string): Base URL for the transcriber failover API backend, SSRF-validated.failoverWhisperApiKey(string or null): API key for the transcriber failover API backend. Omit to leave unchanged; null or empty to clear.failoverWhisperApiModel(string): Model name for the transcriber failover API backend.failoverWhisperApiTimeoutSeconds(integer): Request timeout for the transcriber failover API backend.failoverWhisperMaxAttempts(one of integer, null, constant ``): Upload attempts for the failover transcriber, including the first upload. Null or blank inherits whisperMaxAttempts; omit to leave unchanged.failoverWhisperLanguage(string): Language for the transcriber failover. Empty means same as the active transcriber; otherwise ‘auto’ or a 2-3 letter code.failoverProbeIntervalMinutes(integer): Minutes between health probes of a failover target.failoverRecoveryProbes(integer): Consecutive healthy probes needed before an automatic failover recovers.whisperMaxAttempts(integer): Max attempts for a single whisper API transcription request before giving up.detectionTemperature(number): Temperature (0.0-2.0) for the first-pass ad detection LLM call.detectionMaxTokens(integer): Maximum response tokens (128-32768) for first-pass ad detection.detectionReasoningBudget(integer or null): Manual Anthropic thinking budget (1024-65536 tokens). Used only when the routed model accepts manual budgets; otherwise kept as a dormant setting.detectionReasoningLevel(string or null, one ofnone,low,medium,high,null): Effort for adaptive-only Anthropic models and other providers. Null restores the default. none disables thinking where supported, or uses low effort on models that require thinking.verificationTemperature(number): Temperature (0.0-2.0) for the verification (pass 2) LLM call.verificationMaxTokens(integer): Maximum response tokens (128-32768) for the verification pass.verificationReasoningBudget(integer or null): Manual Anthropic thinking budget for verification pass. Dormant when the routed model uses effort or another provider.verificationReasoningLevel(string or null, one ofnone,low,medium,high,null): Reasoning effort for verification pass, including adaptive-only Anthropic models. Dormant when the routed model uses a manual budget.reviewerTemperature(number): Temperature (0.0-2.0) for both reviewer passes.reviewerMaxTokens(integer): Maximum response tokens (128-32768) for the reviewer.reviewerReasoningBudget(integer or null): Manual Anthropic thinking budget for the reviewer. Dormant when the routed model uses effort or another provider.reviewerReasoningLevel(string or null, one ofnone,low,medium,high,null): Reasoning effort for the reviewer, including adaptive-only Anthropic models. Dormant when the routed model uses a manual budget.chapterBoundaryTemperature(number): Temperature (0.0-2.0) for chapter boundary detection.chapterBoundaryMaxTokens(integer): Maximum response tokens (128-32768) for chapter boundary detection.chapterBoundaryReasoningBudget(integer or null): Manual Anthropic thinking budget for chapter boundary detection. Dormant when the routed model uses effort or another provider.chapterBoundaryReasoningLevel(string or null, one ofnone,low,medium,high,null): Reasoning effort for chapter boundary detection, including adaptive-only Anthropic models. Dormant when the routed model uses a manual budget.chapterTargetSeconds(integer): Target chapter length in seconds (120-3600, default 600). Sets how many boundaries each transcript window is asked for. Must not exceed chapterWindowSeconds.chapterWindowSeconds(integer): Transcript window per boundary-detection call in seconds (600-10800, default 2700). One LLM call per window.chapterMaxBoundaries(integer): Ceiling on chapter boundaries per episode (1-200, default 40).chapterMinDurationSeconds(integer): Shortest chapter in seconds (30-900, default 180). Shorter chapters are absorbed into the previous one. Must not exceed chapterTargetSeconds.chapterTitleTemperature(number): Temperature (0.0-2.0) for chapter title generation.chapterTitleMaxTokens(integer): Maximum response tokens (128-32768) for chapter title generation.chapterTitleReasoningBudget(integer or null): Manual Anthropic thinking budget for chapter title generation. Dormant when the routed model uses effort or another provider.chapterTitleReasoningLevel(string or null, one ofnone,low,medium,high,null): Reasoning effort for chapter title generation, including adaptive-only Anthropic models. Dormant when the routed model uses a manual budget.ollamaNumCtx(integer or null): Ollama context window in tokens (512-131072). Only valid when llmProvider is ollama.windowSizeSeconds(integer or null): Detection-window duration in seconds. Must be strictly greater than windowOverlapSeconds.windowOverlapSeconds(integer or null): Overlap between consecutive detection windows in seconds. Must be less than windowSizeSeconds.verificationMissHoldMinConfidence(number (float)): Confidence floor for a standalone pass-2 verification miss (a catch overlapping no pass-1 marker) to hold for review instead of being dropped (default 0.60).verificationMissAutocutMinConfidence(number (float)): Confidence floor for a standalone verification miss to cut automatically instead of holding for review. 0 disables autocut (default); otherwise must be between 0.5 and 1.0.learningMinConfidence(number (float)): Minimum confidence for a detection to teach the pattern matcher a new sponsor, for ads up to 90 seconds (default 0.85).learningMinConfidenceLong(number (float)): Same floor as learningMinConfidence for ads longer than 90 seconds (default 0.92).learningMinPatternDuration(integer): Shortest span, in seconds, that can teach the pattern matcher. Below this a span is usually a fragment or a host mention rather than a read (default 15).learningMaxPatternDuration(integer): Longest span, in seconds, learned as a single pattern. A longer span is split at its ad transitions and each piece is learned separately (default 120).daiDifferentialOverridesKeep(boolean): A marker overlapping a measured differential region is cut even when segmentCategoryActions maps its category to keep. Audio that differs between two fetches of the same enclosure was inserted, whatever the category says (default true).spliceVetoEnabled(boolean): Hold a long cut found only in the transcript for review unless the audio shows an edit point at its edges. A feed’s own spliceVetoEnabled overrides this (default true).differentialMeasuredCorrMax(number (float)): A cross-fetch region becomes a differential candidate only when its measured correlation is at or below this value (default 0.60).differentialHoldMinSeconds(number (float)): Shortest uncorroborated differential candidate that may hold for review; shorter candidates are dropped. 0 disables the floor (default 10).
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Settings updated | object |
| 400 | Bad request - invalid input | Error |
200 response body
Section titled “200 response body”message(string)
POST /settings/ad-detection/reset
Section titled “POST /settings/ad-detection/reset”Reset ad detection settings
Reset all ad detection settings to their default values, including LLM provider and base URL (reverts to environment variable defaults)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Settings reset to defaults | object |
200 response body
Section titled “200 response body”message(string)
POST /settings/prompts/reset
Section titled “POST /settings/prompts/reset”Reset prompts only
Reset only the system prompts to defaults without affecting model selection or other settings
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Prompts reset to defaults | object |
200 response body
Section titled “200 response body”message(string)
POST /settings/prompts/{name}/reset
Section titled “POST /settings/prompts/{name}/reset”Reset a single prompt
Reset one prompt (system, verification, review, resurrect, chapter, or pattern_cleanup) and its override, when it has one, to defaults
Parameters
Section titled “Parameters”name(path, string, one ofsystem,verification,review,resurrect,chapter,pattern_cleanup, required): Prompt to reset
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Prompt reset to default | object |
| 404 | Resource not found | Error |
200 response body
Section titled “200 response body”value(string)isDefault(boolean)
GET /settings/models
Section titled “GET /settings/models”Get available AI models
Returns list of available AI models for ad detection. Optionally accepts a provider query param to preview models for a different provider before saving settings.
Parameters
Section titled “Parameters”provider(query, string, one ofanthropic,openrouter,openai-compatible,ollama,typesafe,systemone-compatible): Preview models for this provider without saving settings. Valid values are anthropic, openrouter, openai-compatible, ollama, typesafe, systemone-compatible.slot(query, string, one ofprimary,secondary,failover,a,b): Which credential slot to previewproviderwith.secondaryuses the secondary provider slot’s own API key and base URL instead of the primary slot’s;failoverlikewise uses the LLM failover slot’s own key and base URL.a/bare accepted as aliases ofprimary/secondary. Only meaningful together withprovider.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | List of available models | object |
| 400 | Invalid provider or slot parameter | object |
200 response body
Section titled “200 response body”models(array of Model)
400 response body
Section titled “400 response body”error(string)
POST /settings/models/refresh
Section titled “POST /settings/models/refresh”Refresh available AI models
Force refresh the model list for one credential slot. Recreates that slot’s LLM client and fetches a fresh model list. The optional body selects the slot; with no body the primary slot is refreshed.
Request body (application/json)
Section titled “Request body (application/json)”slot(string, one ofprimary,secondary,failover,a,b): Credential slot to refresh. secondary/failover require a configured secondary/failover provider.a/bare accepted as aliases ofprimary/secondary. Defaultprimary.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Refreshed model list | object |
| 400 | Invalid slot, or secondary requested with no secondary provider configured | object |
200 response body
Section titled “200 response body”models(array of Model)count(integer): Number of models available
400 response body
Section titled “400 response body”error(string)
GET /settings/whisper-models
Section titled “GET /settings/whisper-models”Get available Whisper models
Returns list of available Whisper models for transcription with resource requirements
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | List of available Whisper models | object |
200 response body
Section titled “200 response body”models(array of WhisperModel)
GET /settings/provider-budget
Section titled “GET /settings/provider-budget”Read provider admission settings
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Settings and current reservation totals | ProviderBudget |
PUT /settings/provider-budget
Section titled “PUT /settings/provider-budget”Update provider admission settings
Provider charges and enforcement remain in USD. A non-USD save uses the current server-fetched reference rate and records its currency and date.
Request body (application/json, required)
Section titled “Request body (application/json, required)”Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated settings and reservation totals | ProviderBudget |
| 400 | Bad request - invalid input | Error |
| 429 | Update limit of 10 requests per hour exceeded | Error |
| 503 | Currency service unavailable for a non-USD update | Error |
GET /settings/provider-budget/currencies
Section titled “GET /settings/provider-budget/currencies”List provider budget display currencies
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Supported ISO currency codes | object |
| 503 | Currency service unavailable |
200 response body
Section titled “200 response body”currencies(array of CurrencyOption, required)
GET /settings/provider-budget/rate/{currency}
Section titled “GET /settings/provider-budget/rate/{currency}”Read the current USD reference rate for a display currency
Parameters
Section titled “Parameters”currency(path, string, required)from(query, string): Existing display currency for preview conversion.fromRate(query, string): Positive decimal local-per-USD rate for the existing currency.dailyLimit(query, string): Non-negative decimal amount to convert for preview.unknownReserve(query, string): Non-negative decimal amount to convert for preview.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Current USD reference rate and source date | ProviderBudgetRate |
| 400 | Bad request - invalid input | Error |
| 503 | Currency service unavailable |
GET /settings/retention
Section titled “GET /settings/retention”Get retention configuration
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Retention settings | object |
200 response body
Section titled “200 response body”retentionDays(integer)originalRetentionDays(integer): Days to retain the pre-cut original copy of each episode. Defaults to retentionDays when never set; the server clamps to retentionDays on save so an original cannot outlive its processed peer.enabled(boolean)
PUT /settings/retention
Section titled “PUT /settings/retention”Update retention configuration
Request body (application/json, required)
Section titled “Request body (application/json, required)”retentionDays(integer, required): Days to retain processed audio. 0 disables retention.originalRetentionDays(integer): Optional. Days to retain the pre-cut original copy. Server clamps to retentionDays. When omitted, the existing value is preserved.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Retention updated | object |
200 response body
Section titled “200 response body”retentionDays(integer)originalRetentionDays(integer)enabled(boolean)
GET /settings/offline-queue
Section titled “GET /settings/offline-queue”Get offline queue configuration
Offline queue for LLM/Whisper outages. When enabled, an episode that fails because the LLM provider or Whisper API endpoint is unreachable is deferred instead of failed, then processed automatically once the service is reachable again.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Offline queue settings | object |
200 response body
Section titled “200 response body”enabled(boolean)ttlHours(integer): Hours a deferred episode waits before being marked permanently failed.deferredCount(integer): Episodes currently waiting in the offline queue.
PUT /settings/offline-queue
Section titled “PUT /settings/offline-queue”Update offline queue configuration
Request body (application/json, required)
Section titled “Request body (application/json, required)”enabled(boolean): Defer episodes when the LLM or Whisper endpoint is unreachable. Off by default.ttlHours(integer): Hours a deferred episode waits before being marked permanently failed. Expired episodes are logged and removed from the queue.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Offline queue settings updated | object |
200 response body
Section titled “200 response body”enabled(boolean)ttlHours(integer)deferredCount(integer)
GET /settings/rate-limit-hold
Section titled “GET /settings/rate-limit-hold”Get rate-limit hold configuration
Rate-limit queue hold (#696). When enabled, a provider 429 carrying a reset time sends the episode back to the queue and pauses new queue claims until the reset instead of failing the job.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Rate-limit hold settings | object |
200 response body
Section titled “200 response body”enabled(boolean)holdUntil(string or null (date-time)): Until when new queue claims pause; null when no hold is active.llmUsageUrl(string): Operator-configured provider usage/limit endpoint the hold probe checks first; empty when unset.rateLimitProbeMinutes(integer): Minutes between hold probes; 0 disables probing.
PUT /settings/rate-limit-hold
Section titled “PUT /settings/rate-limit-hold”Update rate-limit hold configuration
Request body (application/json, required)
Section titled “Request body (application/json, required)”enabled(boolean): Pause the queue on a provider 429 with a reset time longer than five minutes. Off by default; setting false clears an active pause.llmUsageUrl(string): Provider usage/limit endpoint the hold probe checks first (tier 1); an empty string clears it. Must pass the same SSRF check as the other operator-configured base URLs.rateLimitProbeMinutes(integer): Minutes between hold probes; 0 disables probing.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Rate-limit hold settings updated | object |
200 response body
Section titled “200 response body”enabled(boolean)holdUntil(string or null (date-time))llmUsageUrl(string)rateLimitProbeMinutes(integer)
POST /settings/rate-limit-hold/reset
Section titled “POST /settings/rate-limit-hold/reset”Clear active rate-limit holds without disabling the feature
Lifts every active provider rate-limit hold while leaving the feature enabled. Use after switching models or providers, whose limits differ from the endpoint that produced the hold (#747).
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Holds cleared; current rate-limit hold settings | object |
200 response body
Section titled “200 response body”enabled(boolean)holdUntil(string or null (date-time))llmUsageUrl(string)rateLimitProbeMinutes(integer)
GET /settings/whisper/capacity
Section titled “GET /settings/whisper/capacity”Resolved Whisper pool capacity and in-flight counts
What the operator configured, whether the pool is active (enabled and the backend is the remote OpenAI-compatible API), and what is currently in flight against it. Read from this process’s own pool instance; the queue leader refreshes its own pool every dispatcher pass, so no cross-process signal is needed.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Pool state | object |
200 response body
Section titled “200 response body”enabled(boolean)backend(string)active(boolean): enabled and backend is openai-apiinactiveReason(string or null, one ofdisabled,local_backend,null)capacity(integer)inFlight(integer)transcribingEpisodes(integer)maxEpisodes(object)maxEpisodes.configured(integer)maxEpisodes.effective(integer)chunkWorkers(object)chunkWorkers.configured(integer)chunkWorkers.effective(integer)worstCaseInFlight(integer)exceedsCapacity(boolean)leader(boolean): Whether this process is the background leader that runs transcriptions. inFlight/transcribingEpisodes are only meaningful when true; other workers always report 0.health(WhisperHealthProbe)
GET /settings/youtube/sb-user-id
Section titled “GET /settings/youtube/sb-user-id”Reveal the stored SponsorBlock private user ID
Returns the stored ID so it can be copied, for example into the SponsorBlock extension. Returns 404 with error “not_configured” when none is stored. Limited to 10 requests per minute.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | The stored ID | object |
| 404 | No ID is stored (error “not_configured”) |
200 response body
Section titled “200 response body”sbUserId(string, required)
POST /settings/youtube/sb-user-id/generate
Section titled “POST /settings/youtube/sb-user-id/generate”Generate a SponsorBlock private user ID
Creates a random 48 character SponsorBlock private user ID and stores it encrypted. Returns 409 with error “configured” when an ID is already stored; it cannot be regenerated while one is stored. Returns 409 with error “provider_crypto_unavailable” when secret encryption is unavailable, as PUT does. Limited to 10 requests per minute. Read the stored ID with GET /settings/youtube/sb-user-id.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | The new ID | object |
| 409 | Error “configured” when an ID is already stored, or “provider_crypto_unavailable” when secret encryption is unavailable |
200 response body
Section titled “200 response body”sbUserId(string, required)
POST /settings/youtube/reset
Section titled “POST /settings/youtube/reset”Reset YouTube settings to defaults
Restores the YouTube tunables, proxy and extra yt-dlp arguments to their defaults.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | YouTube settings after the reset | YoutubeSettings |
GET /settings/youtube
Section titled “GET /settings/youtube”Get YouTube feed settings
Global YouTube feed settings. The Data API key is write-only and reported as apiKeyConfigured.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | YouTube settings | YoutubeSettings |
PUT /settings/youtube
Section titled “PUT /settings/youtube”Update YouTube feed settings
Accepts any subset of the fields. Every value is validated before any is written. apiKey set to an empty string or null clears the stored key. Returns 409 when a key is supplied but secret encryption is unavailable.
Request body (application/json, required)
Section titled “Request body (application/json, required)”enabled(boolean)apiKey(string or null)sbSubmitEnabled(boolean): Show the manual SponsorBlock submission review on YouTube episode pages. Nothing is ever sent automatically.sbUserId(string or null): Private SponsorBlock user ID, 32 to 200 printable ASCII characters. Empty or null clears it. Returns 409 when secret encryption is unavailable.processDelayMinutes(integer)sbProbeAttempts(integer)sbProbeIntervalMinutes(integer)sbWatchIntervalHours(integer)sbWatchMaxDays(integer)llmFallback(boolean)captionsAsTranscript(boolean)hidePrivate(boolean)transcribeFallback(boolean)proxyUrl(string): Proxy for yt-dlp and YouTube Data API requests. http, https, socks4, socks5 and socks5h schemes.ytdlpExtraArgs(string): Extra command line arguments added before the app’s own options on every yt-dlp call. Options that run or load other programs, change where files are written, read local files, alter the JSON output, update the binary or carry passwords are rejected; use long option names.ytdlpAutoUpdate(boolean)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated YouTube settings | YoutubeSettings |
| 400 | A value is out of range or has the wrong type | |
| 409 | Secret encryption is unavailable (error “provider_crypto_unavailable”), or sbUserId was sent while a generated ID is stored (error “generated”) |
GET /settings/update-check
Section titled “GET /settings/update-check”Get update-check settings
Controls the daily background check that fires the Update Available notification and the manual GET /system/updates lookup’s channel.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Update-check settings | object |
200 response body
Section titled “200 response body”enabled(boolean): Whether the daily background update check runs.channel(string, one ofstable,edge)
PUT /settings/update-check
Section titled “PUT /settings/update-check”Update update-check settings
Request body (application/json, required)
Section titled “Request body (application/json, required)”enabled(boolean): Enable or disable the daily background update check.channel(string, one ofstable,edge): Release channel to track for update availability.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Update-check settings updated | object |
| 400 | Invalid channel | Error |
200 response body
Section titled “200 response body”enabled(boolean)channel(string, one ofstable,edge)
GET /settings/audio
Section titled “GET /settings/audio”Get audio-related settings
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Audio settings | object |
200 response body
Section titled “200 response body”keepOriginalAudio(boolean): When true, the pre-cut audio file is retained alongside the processed output for the Ad Editor’s Review mode. Roughly doubles per-episode storage.
PUT /settings/audio
Section titled “PUT /settings/audio”Update audio-related settings
Request body (application/json, required)
Section titled “Request body (application/json, required)”keepOriginalAudio(boolean, required)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Audio settings updated | object |
| 400 | Validation error | Error |
200 response body
Section titled “200 response body”keepOriginalAudio(boolean)
GET /settings/processing-timeouts
Section titled “GET /settings/processing-timeouts”Get processing timeout configuration
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Processing timeouts | object |
200 response body
Section titled “200 response body”softTimeoutSeconds(integer)hardTimeoutSeconds(integer)defaults(object)defaults.softTimeoutSeconds(integer)defaults.hardTimeoutSeconds(integer)limits(object)limits.softMin(integer)limits.hardMax(integer)
PUT /settings/processing-timeouts
Section titled “PUT /settings/processing-timeouts”Update processing timeout configuration
Request body (application/json, required)
Section titled “Request body (application/json, required)”softTimeoutSeconds(integer, required)hardTimeoutSeconds(integer, required)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Processing timeouts updated | object |
| 400 | Validation error | Error |
200 response body
Section titled “200 response body”softTimeoutSeconds(integer)hardTimeoutSeconds(integer)
GET /settings/webhooks
Section titled “GET /settings/webhooks”List webhooks
Returns all configured webhooks
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Webhook list | object |
200 response body
Section titled “200 response body”webhooks(array of Webhook)
POST /settings/webhooks
Section titled “POST /settings/webhooks”Create a webhook
Create a new webhook subscription for event notifications
Request body (application/json, required)
Section titled “Request body (application/json, required)”url(string (uri), required): URL to receive webhook POST requestsevents(array of string, one ofEpisode Processed,Episode Awaiting Approval,Episode Pending Review,Episode Failed,Auth Failure,Limit Exceeded,Rate Limit Structural,Feed Refresh Failed,Update Available,Cue Template Quiet,Queue Held,Queue Resumed,Service Offline,Service Reachable,Failover Triggered,Failover Cancelled, required): List of event types to subscribe to.Episode Awaiting Approvalfires instead ofEpisode Processedwhen a finished episode is held for manual approval.Episode Pending Reviewfires alongside the processed or awaiting-approval event when a finished episode has detections held for review.Auth Failurefires on credential errors (invalid or expired API key).Limit Exceededfires when the provider rejects a request because a spend or usage limit is exhausted (monthly key limit, out of credits).Rate Limit Structuralfires when one detection-window request exceeds the provider’s per-minute token cap.Feed Refresh Failedfires after several consecutive failures to fetch a podcast’s origin RSS feed.Update Availablefires when the daily update check finds a newer release on the selected channel.Cue Template Quietfires when an enabled cue template on a cue_only feed stops matching recent episodes.Queue Heldfires when a provider rate limit pauses the queue;Queue Resumedwhen that hold clears.Service Offlinefires when an episode defers because the LLM or Whisper endpoint is unreachable;Service Reachablewhen the probe finds it back up.Failover Triggeredfires when a target switches to its failover provider;Failover Cancelledwhen it switches back.enabled(boolean, required): Whether the webhook is activesecret(string): Optional shared secret for HMAC signature verificationpayloadTemplate(string or null): Optional custom payload templatecontentType(string): Content type for webhook delivery (e.g. application/json)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 201 | Webhook created | Webhook |
| 400 | Bad request - invalid input | Error |
PUT /settings/webhooks/{webhook_id}
Section titled “PUT /settings/webhooks/{webhook_id}”Update a webhook
Update an existing webhook configuration
Parameters
Section titled “Parameters”webhook_id(path, string, required): Webhook identifier
Request body (application/json, required)
Section titled “Request body (application/json, required)”url(string (uri))events(array of string)enabled(boolean)secret(string)payloadTemplate(string or null)contentType(string)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Webhook updated | Webhook |
| 404 | Resource not found | Error |
DELETE /settings/webhooks/{webhook_id}
Section titled “DELETE /settings/webhooks/{webhook_id}”Delete a webhook
Remove a webhook subscription
Parameters
Section titled “Parameters”webhook_id(path, string, required): Webhook identifier
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Webhook deleted | object |
200 response body
Section titled “200 response body”message(string)
POST /settings/webhooks/{webhook_id}/test
Section titled “POST /settings/webhooks/{webhook_id}/test”Test a webhook
Send one test payload per event the webhook is subscribed to, matching each event’s real payload shape (test: true set on every payload). Falls back to a single Episode Processed sample when the webhook’s event list is empty. If a custom payload template cannot render for an event, the default payload is used (a readable line for text/* content types, JSON otherwise) and the result is flagged templateFallback, regardless of delivery status. Rate limited to 10/min.
Parameters
Section titled “Parameters”webhook_id(path, string, required): Webhook identifier
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Test result | object |
| 404 | Resource not found | Error |
| 429 | Rate limit exceeded | Error |
200 response body
Section titled “200 response body”success(boolean): true only when every event deliveredresults(array of object)results[].event(string)results[].delivered(boolean)results[].templateFallback(boolean): true when the custom payload template could not render for this event and the default payload was used, independent of delivery statusmessage(string): Summary, e.g. “3 of 3 test payloads delivered”. Names the events whose template fell back, if any.
POST /settings/webhooks/validate-template
Section titled “POST /settings/webhooks/validate-template”Validate a webhook payload template
Validate a custom payload template and return a preview of the rendered output. Rate limited to 30/min.
Request body (application/json, required)
Section titled “Request body (application/json, required)”template(string, required): The payload template string to validate
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Validation result | object |
| 429 | Rate limit exceeded | Error |
200 response body
Section titled “200 response body”valid(boolean)preview(string): Rendered template preview (when valid)error(string or null): Error message (when invalid)
GET /settings/notifications/timezone
Section titled “GET /settings/notifications/timezone”Get the notification timezone
Returns the IANA zone used to compute timestamp_local in webhook and email notifications. Defaults to UTC, or the container’s TZ env var when it names a valid zone.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Notification timezone | NotificationTimezoneSettings |
PUT /settings/notifications/timezone
Section titled “PUT /settings/notifications/timezone”Set the notification timezone
Rejects a timezone name zoneinfo cannot resolve.
Request body (application/json, required)
Section titled “Request body (application/json, required)”Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated timezone | NotificationTimezoneSettings |
| 400 | Unknown timezone name | Error |
GET /settings/notifications/email
Section titled “GET /settings/notifications/email”Get email notification settings
Returns the email notification configuration. The SMTP password is never included; only the smtpPasswordConfigured flag.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Email notification settings | EmailNotificationSettings |
PUT /settings/notifications/email
Section titled “PUT /settings/notifications/email”Update email notification settings
Partial update. smtpPassword is write-only: send a value to set it, an empty string to clear it, or omit the field to keep the stored password. Setting enabled to true requires an SMTP host, from address, and at least one recipient.
Request body (application/json, required)
Section titled “Request body (application/json, required)”EmailNotificationSettingsUpdate
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated settings | EmailNotificationSettings |
| 400 | Validation error | Error |
| 409 | Secret store unavailable (set MINUSPOD_MASTER_PASSPHRASE) | Error |
POST /settings/notifications/email/test
Section titled “POST /settings/notifications/email/test”Send a test email
Sends a test email using the saved settings. Rate limited to 10/min.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Test outcome | object |
| 429 | Rate limit exceeded | Error |
200 response body
Section titled “200 response body”success(boolean)message(string)
GET /podping/nodes
Section titled “GET /podping/nodes”Get configured Podping RPC servers
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Effective server list and shipped defaults | PodpingNodeSettings |
PUT /podping/nodes
Section titled “PUT /podping/nodes”Replace the Podping RPC server list
Normalizes and deduplicates endpoint URLs. Changes apply to the running listener without resetting its block position. URLs must use HTTP or HTTPS and cannot contain credentials, query strings, or fragments.
Request body (application/json, required)
Section titled “Request body (application/json, required)”nodes(PodpingNodeUrls, required)
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Saved server list | PodpingNodeSettings |
| 400 | Invalid or empty server list | Error |
POST /podping/nodes/reset
Section titled “POST /podping/nodes/reset”Restore the default Podping RPC servers
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Shipped server list restored | PodpingNodeSettings |
GET /settings/reviewer
Section titled “GET /settings/reviewer”Read ad-reviewer auto-update settings
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Current settings | object |
200 response body
Section titled “200 response body”updatePatternsFromReviewerAdjustments(boolean)minTrimThreshold(number)parallelAds(integer): Number of ads the reviewer reviews concurrently via the LLM. 1 reviews them one at a time.parallelAdsDefault(integer): Default value of parallelAds (env-backed registry).
PUT /settings/reviewer
Section titled “PUT /settings/reviewer”Update ad-reviewer auto-update settings
Request body (application/json, required)
Section titled “Request body (application/json, required)”updatePatternsFromReviewerAdjustments(boolean)minTrimThreshold(number)parallelAds(integer): Number of ads the reviewer reviews concurrently. Range 1-32, default 4.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated settings | object |
200 response body
Section titled “200 response body”updatePatternsFromReviewerAdjustments(boolean)minTrimThreshold(number)parallelAds(integer)parallelAdsDefault(integer)
GET /settings/community-sync
Section titled “GET /settings/community-sync”Read community-pattern sync settings
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Current settings | CommunitySyncSettings |
PUT /settings/community-sync
Section titled “PUT /settings/community-sync”Update community-pattern sync settings
Request body (application/json, required)
Section titled “Request body (application/json, required)”enabled(boolean)cron(string)categories(array of string, one ofsponsor,cross_promo,self_promo,interaction,intro,outro,recap): Segment categories to accept from community sync. Unchecking a category deactivates (not deletes) its already-synced community patterns; re-checking it reactivates them on the next sync.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated settings | CommunitySyncSettings |
GET /settings/replacement-audio
Section titled “GET /settings/replacement-audio”Read the replacement audio currently in use
Metadata for the audio spliced in wherever an ad was cut. source is
uploaded when an operator has supplied their own file, default
otherwise.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Current replacement audio | ReplacementAudio |
POST /settings/replacement-audio
Section titled “POST /settings/replacement-audio”Upload a replacement audio file
Accepts a single audio file as multipart field file, transcodes it to
MP3 and installs it on the data volume so it survives a redeploy. Takes
effect on the next episode without a restart. Rejects files over 5 MB or
30 seconds; every cut becomes as long as this file.
Request body (multipart/form-data, required)
Section titled “Request body (multipart/form-data, required)”file(string (binary), required): MP3, WAV, M4A, OGG or FLAC. Up to 5 MB and 30 seconds.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Installed, returning the new metadata | ReplacementAudio |
| 400 | Rejected, with an operator-facing reason | Error |
DELETE /settings/replacement-audio
Section titled “DELETE /settings/replacement-audio”Restore the default replacement audio
Removes an uploaded file so the shipped default applies again.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Current replacement audio after the revert | ReplacementAudio plus object |
200 response body
Section titled “200 response body”Includes every field of ReplacementAudio.
reverted(boolean): False when no uploaded file was present.
GET /settings/replacement-audio/file
Section titled “GET /settings/replacement-audio/file”Download the replacement audio for preview
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | The audio file | audio/mpeg |
| 404 | No replacement audio is installed | Error |
GET /settings/db-backup
Section titled “GET /settings/db-backup”Read scheduled DB backup settings
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Current settings | DatabaseBackupSettings |
PUT /settings/db-backup
Section titled “PUT /settings/db-backup”Update scheduled DB backup settings
Request body (application/json, required)
Section titled “Request body (application/json, required)”enabled(boolean)cron(string): Standard 5-field cron expression. Must be a valid cron string.dest(string): Absolute path to the backup directory. Empty string resets to the default location (<data_dir>/backups).keepCount(integer): Number of backup files to retain. 1 uses overwrite mode (fixed filename); 2 or more uses rotate mode (timestamped filenames, oldest pruned).
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated settings | DatabaseBackupSettings |
| 400 | Invalid cron expression, keepCount out of range, or invalid dest path | Error |
PUT /settings/pattern-cleanup
Section titled “PUT /settings/pattern-cleanup”Update pattern cleanup (Experiments) settings
Request body (application/json, required)
Section titled “Request body (application/json, required)”enabled(boolean)cron(string): Valid 5-field cron expression in UTC.batchSize(integer)unusedDays(integer)provider(string or null): ‘primary’/‘secondary’ (or the ‘a’/‘b’ aliases) or ‘same_as_detection’; blank or null clears the override so detection’s slot is inherited.model(string or null): Blank or null inherits the detection model.
Responses
Section titled “Responses”| Status | Description | Body |
|---|---|---|
| 200 | Updated settings | PatternCleanupSettings |
| 400 | Malformed or empty request object, invalid field type, invalid cron, out-of-range value, unknown provider slot, or model too long | Error |