diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1165ba..597c0b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Fixed
- Fixed playlist schedule summaries showing weekday text and omitting boundary times when dates are returned as `Date` objects.
+- Preserved saved API region items-path overrides when reopening the slide editor instead of replacing them with the source default.
## 2.20.4 - 2026-10-25
diff --git a/src/web/public/js/regions/type/api.js b/src/web/public/js/regions/type/api.js
index d253987..dae5168 100644
--- a/src/web/public/js/regions/type/api.js
+++ b/src/web/public/js/regions/type/api.js
@@ -227,7 +227,9 @@
var sourceOptions = String(context.sourceOptions || '');
var itemsPathValue = context.itemsPath !== undefined
? String(context.itemsPath === null ? '' : context.itemsPath)
- : String(config.default_items_path || '');
+ : config.items_path !== undefined
+ ? String(config.items_path === null ? '' : config.items_path)
+ : String(config.default_items_path || '');
var sampleDataPreview = String(context.sampleDataPreview || '');
return '' +
'
' +
diff --git a/test/api-region-sort.test.js b/test/api-region-sort.test.js
index 65478d1..c50ab98 100644
--- a/test/api-region-sort.test.js
+++ b/test/api-region-sort.test.js
@@ -242,6 +242,32 @@ test('API region results panel exposes its open control without resize buttons',
assert.match(expandedMarkup, /name="region_api_sort_by_24"[^>]*value="stats\.kills"/);
});
+test('API editor preserves saved item paths instead of replacing them with the source default', () => {
+ const editor = loadApiEditor();
+ const context = {
+ region: { id: 23 },
+ current: { value: '{{0.name}}' },
+ config: { source_id: 11, default_items_path: 'data' },
+ sourceOptions: '',
+ placeholderFields: [],
+ placeholderChips: '',
+ sampleDataPreview: '[]',
+ fontSize: 32
+ };
+
+ for (const [savedPath, expectedPath] of [['data.players', 'data.players'], ['', ''], [null, ''], [undefined, 'data']]) {
+ const markup = editor.renderEditorCard({ ...context, config: { ...context.config, items_path: savedPath } });
+ assert.match(markup, new RegExp('name="region_api_items_path_23"[^>]*value="' + expectedPath.replace('.', '\\.') + '"'));
+ }
+
+ const explicitContext = editor.renderEditorCard({
+ ...context,
+ itemsPath: 'other.players',
+ config: { ...context.config, items_path: 'data.players' }
+ });
+ assert.match(explicitContext, /name="region_api_items_path_23"[^>]*value="other\.players"/);
+});
+
test('API results preview always includes the first result completely', () => {
let source = fs.readFileSync(path.join(__dirname, '../src/web/public/js/slides/slide-form-regions.js'), 'utf8');
source = source.replace("import { createSlideFormPreviewHelpers } from '/assets/js/slides/slide-form-preview.js';", '').replace('export function createSlideFormRegionHelpers', 'function createSlideFormRegionHelpers');