diff --git a/apps/docs/docs/management/api/index.mdx b/apps/docs/docs/management/api/index.mdx
index c01e17bdf..2e9c4c6f5 100644
--- a/apps/docs/docs/management/api/index.mdx
+++ b/apps/docs/docs/management/api/index.mdx
@@ -10,6 +10,16 @@ Use the generated specification under **Management → Tools → API** or the [i
This page covers contracts that are not obvious from those schemas.
The in-app reference loads its schema from `/api/openapi?format=scalar` on your Homarr instance. `/api/openapi` provides the raw JSON specification.
+REST exposes selected existing Homarr controls. Use tRPC for advanced operations; both accept the `ApiKey` header.
+`PUT /api/boards/{id}/content` uses the dashboard editor's complete `sections` and `items` arrays. Omitted entries are
+removed, so read the current content through tRPC `board.getBoardByName` before editing it. Widget movement and
+resizing can instead use the existing item-layout endpoint.
+
+Use `GET /api/users/preferences` and `PATCH /api/users/preferences` to read or change preferences. Send only changed
+values: `{ "colorScheme": "light" }` or `{ "headerPreferences": { "visible": false } }`. Omitted values stay unchanged;
+`null` clears nullable board/search references. Header fields merge; supplied zone arrays replace their zone.
+Omit `userId` for yourself; another user requires admin permission. Older preference routes remain available.
+

## Authentication and browser access
diff --git a/apps/docs/public/api/open-api-schema.json b/apps/docs/public/api/open-api-schema.json
index e0e67aebb..2f1c81211 100644
--- a/apps/docs/public/api/open-api-schema.json
+++ b/apps/docs/public/api/open-api-schema.json
@@ -9,6 +9,40 @@
"url": "http://localhost:3000"
}
],
+ "tags": [
+ {
+ "name": "boards",
+ "x-displayName": "Build dashboards"
+ },
+ {
+ "name": "apps",
+ "x-displayName": "Manage apps"
+ },
+ {
+ "name": "users",
+ "x-displayName": "Manage users and preferences"
+ },
+ {
+ "name": "settings",
+ "x-displayName": "Configure the instance"
+ },
+ {
+ "name": "integrations",
+ "x-displayName": "Connect integrations"
+ },
+ {
+ "name": "invites",
+ "x-displayName": "Manage invitations"
+ },
+ {
+ "name": "certificates",
+ "x-displayName": "Manage trusted certificates"
+ },
+ {
+ "name": "info",
+ "x-displayName": "Inspect Homarr"
+ }
+ ],
"externalDocs": {
"url": "https://homarr.dev"
},
@@ -2025,6 +2059,246 @@
}
},
"/api/boards/{id}/settings": {
+ "get": {
+ "operationId": "boardRouter-getBoardSettings",
+ "summary": "Read board settings",
+ "description": "Read editable board settings. Requires modify access to the board.",
+ "tags": [
+ "boards"
+ ],
+ "security": [
+ {
+ "apikey": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "string"
+ },
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "pageTitle": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "metaTitle": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "logoImageUrl": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "faviconImageUrl": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "backgroundImageUrl": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "backgroundImageAttachment": {
+ "type": "string",
+ "enum": [
+ "fixed",
+ "scroll"
+ ]
+ },
+ "backgroundImageRepeat": {
+ "type": "string",
+ "enum": [
+ "repeat",
+ "repeat-x",
+ "repeat-y",
+ "no-repeat"
+ ]
+ },
+ "backgroundImageSize": {
+ "type": "string",
+ "enum": [
+ "cover",
+ "contain"
+ ]
+ },
+ "primaryColor": {
+ "type": "string",
+ "pattern": "^#[0-9A-Fa-f]{6}$"
+ },
+ "secondaryColor": {
+ "type": "string",
+ "pattern": "^#[0-9A-Fa-f]{6}$"
+ },
+ "opacity": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 100
+ },
+ "customCss": {
+ "type": "string",
+ "maxLength": 16384
+ },
+ "iconColor": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "itemRadius": {
+ "anyOf": [
+ {
+ "type": "string",
+ "const": "xs"
+ },
+ {
+ "type": "string",
+ "const": "sm"
+ },
+ {
+ "type": "string",
+ "const": "md"
+ },
+ {
+ "type": "string",
+ "const": "lg"
+ },
+ {
+ "type": "string",
+ "const": "xl"
+ }
+ ]
+ },
+ "disableStatus": {
+ "type": "boolean"
+ },
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "pageTitle",
+ "metaTitle",
+ "logoImageUrl",
+ "faviconImageUrl",
+ "backgroundImageUrl",
+ "backgroundImageAttachment",
+ "backgroundImageRepeat",
+ "backgroundImageSize",
+ "primaryColor",
+ "secondaryColor",
+ "opacity",
+ "customCss",
+ "iconColor",
+ "itemRadius",
+ "disableStatus",
+ "id",
+ "name"
+ ],
+ "additionalProperties": false
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.BAD_REQUEST"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization not provided",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.UNAUTHORIZED"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Insufficient access",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.FORBIDDEN"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.NOT_FOUND"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR"
+ }
+ }
+ }
+ }
+ }
+ },
"patch": {
"operationId": "boardRouter-savePartialBoardSettings",
"summary": "Update board settings",
@@ -2257,6 +2531,840 @@
}
}
},
+ "/api/boards/{id}/layouts": {
+ "put": {
+ "operationId": "boardRouter-saveLayouts",
+ "summary": "Save dashboard layouts",
+ "description": "Save the same responsive layout settings used by the dashboard editor. Requires modify access.",
+ "tags": [
+ "boards"
+ ],
+ "security": [
+ {
+ "apikey": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "string"
+ },
+ "required": true
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "layouts": {
+ "minItems": 2,
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 32
+ },
+ "columnCount": {
+ "type": "number",
+ "minimum": 1,
+ "maximum": 24
+ },
+ "leftGutterColumnCount": {
+ "default": 0,
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 3
+ },
+ "rightGutterColumnCount": {
+ "default": 0,
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 3
+ },
+ "breakpoint": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "role": {
+ "type": "string",
+ "enum": [
+ "mobile",
+ "base",
+ "custom"
+ ]
+ }
+ },
+ "required": [
+ "id",
+ "name",
+ "columnCount",
+ "breakpoint",
+ "role"
+ ]
+ }
+ }
+ },
+ "required": [
+ "layouts"
+ ]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "minItems": 2,
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 32
+ },
+ "columnCount": {
+ "type": "number",
+ "minimum": 1,
+ "maximum": 24
+ },
+ "leftGutterColumnCount": {
+ "default": 0,
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 3
+ },
+ "rightGutterColumnCount": {
+ "default": 0,
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 3
+ },
+ "breakpoint": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "role": {
+ "type": "string",
+ "enum": [
+ "mobile",
+ "base",
+ "custom"
+ ]
+ }
+ },
+ "required": [
+ "id",
+ "name",
+ "columnCount",
+ "leftGutterColumnCount",
+ "rightGutterColumnCount",
+ "breakpoint",
+ "role"
+ ],
+ "additionalProperties": false
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.BAD_REQUEST"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization not provided",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.UNAUTHORIZED"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Insufficient access",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.FORBIDDEN"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.NOT_FOUND"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/boards/{boardId}/layouts/{layoutId}/reset": {
+ "post": {
+ "operationId": "boardRouter-resetLayout",
+ "summary": "Reset a dashboard layout",
+ "description": "Reset a Mobile or custom layout from the Base layout. Requires modify access.",
+ "tags": [
+ "boards"
+ ],
+ "security": [
+ {
+ "apikey": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "boardId",
+ "schema": {
+ "type": "string"
+ },
+ "required": true
+ },
+ {
+ "in": "path",
+ "name": "layoutId",
+ "schema": {
+ "type": "string"
+ },
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "content": {
+ "application/json": {
+ "schema": {}
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.BAD_REQUEST"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization not provided",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.UNAUTHORIZED"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Insufficient access",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.FORBIDDEN"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/boards/{id}/content": {
+ "put": {
+ "operationId": "boardRouter-saveBoard",
+ "summary": "Save dashboard content",
+ "description": "Save the editor's complete sections and items arrays, including widget options and placements. Omitted items and sections are removed. Requires modify access; Custom Widget placement changes require admin access.",
+ "tags": [
+ "boards"
+ ],
+ "security": [
+ {
+ "apikey": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "string"
+ },
+ "required": true
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "sections": {
+ "type": "array",
+ "items": {
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string",
+ "const": "empty"
+ },
+ "yOffset": {
+ "type": "integer",
+ "minimum": -9007199254740991,
+ "maximum": 9007199254740991
+ },
+ "xOffset": {
+ "type": "integer",
+ "minimum": -9007199254740991,
+ "maximum": 9007199254740991
+ }
+ },
+ "required": [
+ "id",
+ "kind",
+ "yOffset",
+ "xOffset"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string",
+ "const": "container"
+ },
+ "options": {
+ "default": {
+ "title": "",
+ "customCssClasses": [],
+ "borderColor": "",
+ "showLabel": true,
+ "collapsible": false,
+ "showOpenAll": false,
+ "scrollable": false
+ },
+ "type": "object",
+ "properties": {
+ "title": {
+ "default": "",
+ "type": "string",
+ "maxLength": 64
+ },
+ "customCssClasses": {
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "borderColor": {
+ "default": "",
+ "type": "string"
+ },
+ "showLabel": {
+ "default": true,
+ "type": "boolean"
+ },
+ "collapsible": {
+ "default": false,
+ "type": "boolean"
+ },
+ "showOpenAll": {
+ "default": false,
+ "type": "boolean"
+ },
+ "scrollable": {
+ "default": false,
+ "type": "boolean"
+ }
+ }
+ },
+ "collapsed": {
+ "default": false,
+ "type": "boolean"
+ },
+ "layouts": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "layoutId": {
+ "type": "string"
+ },
+ "yOffset": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "xOffset": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "width": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 32767
+ },
+ "height": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 32767
+ },
+ "parentSectionId": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "layoutId",
+ "yOffset",
+ "xOffset",
+ "width",
+ "height",
+ "parentSectionId"
+ ]
+ }
+ }
+ },
+ "required": [
+ "id",
+ "kind",
+ "layouts"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string",
+ "const": "dynamic"
+ },
+ "options": {
+ "default": {
+ "title": "",
+ "customCssClasses": [],
+ "borderColor": "",
+ "showLabel": true,
+ "collapsible": false,
+ "showOpenAll": false,
+ "scrollable": false
+ },
+ "type": "object",
+ "properties": {
+ "title": {
+ "default": "",
+ "type": "string",
+ "maxLength": 64
+ },
+ "customCssClasses": {
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "borderColor": {
+ "default": "",
+ "type": "string"
+ },
+ "showLabel": {
+ "default": true,
+ "type": "boolean"
+ },
+ "collapsible": {
+ "default": false,
+ "type": "boolean"
+ },
+ "showOpenAll": {
+ "default": false,
+ "type": "boolean"
+ },
+ "scrollable": {
+ "default": false,
+ "type": "boolean"
+ }
+ }
+ },
+ "collapsed": {
+ "default": false,
+ "type": "boolean"
+ },
+ "layouts": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "layoutId": {
+ "type": "string"
+ },
+ "yOffset": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "xOffset": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "width": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 32767
+ },
+ "height": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 32767
+ },
+ "parentSectionId": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "layoutId",
+ "yOffset",
+ "xOffset",
+ "width",
+ "height",
+ "parentSectionId"
+ ]
+ }
+ }
+ },
+ "required": [
+ "id",
+ "kind",
+ "layouts"
+ ]
+ }
+ ]
+ }
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "allOf": [
+ {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "enum": [
+ "stats",
+ "clock",
+ "weather",
+ "airQuality",
+ "countdown",
+ "timer",
+ "app",
+ "iframe",
+ "video",
+ "notebook",
+ "anchorNote",
+ "dnsHoleSummary",
+ "dnsHoleControls",
+ "smartHome-entityState",
+ "smartHome-executeAutomation",
+ "stockPrice",
+ "mediaServer",
+ "calendar",
+ "downloads",
+ "mediaRequests-requestList",
+ "mediaRequests-requestStats",
+ "mediaTranscoding",
+ "mediaMissing",
+ "minecraftServerStatus",
+ "networkControllerSummary",
+ "networkControllerStatus",
+ "rssFeed",
+ "bookmarks",
+ "indexerManager",
+ "healthMonitoring",
+ "releases",
+ "mediaReleases",
+ "dockerContainers",
+ "firewall",
+ "notifications",
+ "systemResources",
+ "coolify",
+ "systemDisks",
+ "timetable",
+ "immich-serverStats",
+ "immich-albumCarousel",
+ "paperlessNgx",
+ "patchmon",
+ "bazarr",
+ "tracearr",
+ "speedtestTracker",
+ "uptimeKuma",
+ "audioStats",
+ "umami",
+ "vpn",
+ "archiveTeamWarrior",
+ "ups",
+ "beszelSystemTable",
+ "beszelSystemGrid",
+ "beszelAlerts",
+ "beszelSystemStats",
+ "traefik",
+ "customApi",
+ "assistant",
+ "wud",
+ "llamacpp",
+ "wazuhAgents",
+ "wazuhAlerts",
+ "wazuhSummary",
+ "wazuhTimeline",
+ "wazuhTopList",
+ "wazuhVulnerabilities",
+ "wazuhFim",
+ "wazuhAuthFailures"
+ ]
+ },
+ "options": {
+ "type": "object",
+ "propertyNames": {
+ "type": "string"
+ },
+ "additionalProperties": {}
+ }
+ },
+ "required": [
+ "kind",
+ "options"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "layouts": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "layoutId": {
+ "type": "string"
+ },
+ "yOffset": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "xOffset": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 32767
+ },
+ "width": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 32767
+ },
+ "height": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 32767
+ },
+ "sectionId": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "layoutId",
+ "yOffset",
+ "xOffset",
+ "width",
+ "height",
+ "sectionId"
+ ]
+ }
+ },
+ "integrationIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "advancedOptions": {
+ "type": "object",
+ "properties": {
+ "title": {
+ "default": null,
+ "anyOf": [
+ {
+ "type": "string",
+ "maxLength": 64
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "customCssClasses": {
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "borderColor": {
+ "default": "",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "required": [
+ "id",
+ "layouts",
+ "integrationIds",
+ "advancedOptions"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "required": [
+ "sections",
+ "items"
+ ]
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "content": {
+ "application/json": {
+ "schema": {}
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.BAD_REQUEST"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization not provided",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.UNAUTHORIZED"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Insufficient access",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.FORBIDDEN"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.NOT_FOUND"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/boards/items": {
"post": {
"operationId": "boardRouter-addItem",
@@ -3845,6 +4953,470 @@
}
}
},
+ "/api/users/preferences": {
+ "get": {
+ "operationId": "userRouter-getPreferences",
+ "summary": "Get user preferences",
+ "description": "Read every preference as structured JSON. Omit userId for the current user; reading another user requires admin permission.",
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "apikey": []
+ }
+ ],
+ "parameters": [
+ {
+ "in": "query",
+ "name": "userId",
+ "schema": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UserPreferences"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.BAD_REQUEST"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization not provided",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.UNAUTHORIZED"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Insufficient access",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.FORBIDDEN"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.NOT_FOUND"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "operationId": "userRouter-updatePreferences",
+ "summary": "Update user preferences",
+ "description": "Update only supplied preferences and return the result. Omitted fields remain unchanged; null clears nullable references. Header fields and zones merge, while each supplied zone array replaces that zone. The complete merged header must retain unique items and account access. New board shortcuts must be accessible to the target user. Omit userId for yourself; another user requires admin permission. All validation completes before one write.",
+ "tags": [
+ "users"
+ ],
+ "security": [
+ {
+ "apikey": []
+ }
+ ],
+ "parameters": [],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "userId": {
+ "type": "string",
+ "minLength": 1
+ },
+ "colorScheme": {
+ "type": "string",
+ "enum": [
+ "auto",
+ "light",
+ "dark"
+ ]
+ },
+ "byteUnitSystem": {
+ "type": "string",
+ "enum": [
+ "binary",
+ "decimal"
+ ]
+ },
+ "firstDayOfWeek": {
+ "type": "number",
+ "enum": [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]
+ },
+ "pingIconsEnabled": {
+ "type": "boolean"
+ },
+ "enableRightClickOnWidgets": {
+ "type": "boolean"
+ },
+ "homeBoardId": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "mobileHomeBoardId": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "defaultSearchEngineId": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 1
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "openSearchInNewTab": {
+ "type": "boolean"
+ },
+ "ddgBangs": {
+ "type": "boolean"
+ },
+ "headerPreferences": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "number",
+ "const": 3
+ },
+ "visible": {
+ "type": "boolean"
+ },
+ "searchDisplay": {
+ "type": "string",
+ "enum": [
+ "input",
+ "icon"
+ ]
+ },
+ "logoDisplay": {
+ "type": "string",
+ "enum": [
+ "logo",
+ "logoAndText"
+ ]
+ },
+ "zones": {
+ "type": "object",
+ "properties": {
+ "left": {
+ "maxItems": 100,
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "builtin"
+ },
+ "id": {
+ "type": "string",
+ "enum": [
+ "logo",
+ "search",
+ "home",
+ "boardSwitcher",
+ "assistant",
+ "docker",
+ "boardEdit",
+ "boardSettings",
+ "settings",
+ "themeToggle",
+ "user"
+ ]
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "board"
+ },
+ "boardId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255
+ }
+ },
+ "required": [
+ "type",
+ "boardId"
+ ],
+ "additionalProperties": false
+ }
+ ]
+ }
+ },
+ "center": {
+ "maxItems": 100,
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "builtin"
+ },
+ "id": {
+ "type": "string",
+ "enum": [
+ "logo",
+ "search",
+ "home",
+ "boardSwitcher",
+ "assistant",
+ "docker",
+ "boardEdit",
+ "boardSettings",
+ "settings",
+ "themeToggle",
+ "user"
+ ]
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "board"
+ },
+ "boardId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255
+ }
+ },
+ "required": [
+ "type",
+ "boardId"
+ ],
+ "additionalProperties": false
+ }
+ ]
+ }
+ },
+ "right": {
+ "maxItems": 100,
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "builtin"
+ },
+ "id": {
+ "type": "string",
+ "enum": [
+ "logo",
+ "search",
+ "home",
+ "boardSwitcher",
+ "assistant",
+ "docker",
+ "boardEdit",
+ "boardSettings",
+ "settings",
+ "themeToggle",
+ "user"
+ ]
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "board"
+ },
+ "boardId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255
+ }
+ },
+ "required": [
+ "type",
+ "boardId"
+ ],
+ "additionalProperties": false
+ }
+ ]
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false,
+ "minProperties": 1,
+ "not": {
+ "required": [
+ "userId"
+ ],
+ "maxProperties": 1
+ }
+ },
+ "example": {
+ "colorScheme": "light"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Successful response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UserPreferences"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid input data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.BAD_REQUEST"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authorization not provided",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.UNAUTHORIZED"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Insufficient access",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.FORBIDDEN"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.NOT_FOUND"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/users/{userId}": {
"get": {
"operationId": "userRouter-getById",
@@ -3942,6 +5514,9 @@
}
]
},
+ "colorScheme": {
+ "type": "string"
+ },
"byteUnitSystem": {
"type": "string",
"enum": [
@@ -3995,6 +5570,7 @@
"provider",
"homeBoardId",
"mobileHomeBoardId",
+ "colorScheme",
"byteUnitSystem",
"firstDayOfWeek",
"pingIconsEnabled",
@@ -5468,8 +7044,15 @@
"properties": {
"firstDayOfWeek": {
"type": "number",
- "minimum": 0,
- "maximum": 6
+ "enum": [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]
},
"id": {
"type": "string"
@@ -6573,6 +8156,366 @@
"issues": []
}
},
+ "UserPreferences": {
+ "type": "object",
+ "properties": {
+ "colorScheme": {
+ "type": "string",
+ "enum": [
+ "auto",
+ "light",
+ "dark"
+ ]
+ },
+ "byteUnitSystem": {
+ "type": "string",
+ "enum": [
+ "binary",
+ "decimal"
+ ]
+ },
+ "firstDayOfWeek": {
+ "type": "number",
+ "enum": [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]
+ },
+ "pingIconsEnabled": {
+ "type": "boolean"
+ },
+ "enableRightClickOnWidgets": {
+ "type": "boolean"
+ },
+ "homeBoardId": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "mobileHomeBoardId": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "defaultSearchEngineId": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 1
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "openSearchInNewTab": {
+ "type": "boolean"
+ },
+ "ddgBangs": {
+ "type": "boolean"
+ },
+ "headerPreferences": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "number",
+ "const": 3
+ },
+ "visible": {
+ "type": "boolean"
+ },
+ "searchDisplay": {
+ "type": "string",
+ "enum": [
+ "input",
+ "icon"
+ ]
+ },
+ "logoDisplay": {
+ "default": "logoAndText",
+ "type": "string",
+ "enum": [
+ "logo",
+ "logoAndText"
+ ]
+ },
+ "zones": {
+ "type": "object",
+ "properties": {
+ "left": {
+ "maxItems": 100,
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "builtin"
+ },
+ "id": {
+ "type": "string",
+ "enum": [
+ "logo",
+ "search",
+ "home",
+ "boardSwitcher",
+ "assistant",
+ "docker",
+ "boardEdit",
+ "boardSettings",
+ "settings",
+ "themeToggle",
+ "user"
+ ]
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "board"
+ },
+ "boardId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255
+ }
+ },
+ "required": [
+ "type",
+ "boardId"
+ ],
+ "additionalProperties": false
+ }
+ ],
+ "type": "object"
+ }
+ },
+ "center": {
+ "maxItems": 100,
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "builtin"
+ },
+ "id": {
+ "type": "string",
+ "enum": [
+ "logo",
+ "search",
+ "home",
+ "boardSwitcher",
+ "assistant",
+ "docker",
+ "boardEdit",
+ "boardSettings",
+ "settings",
+ "themeToggle",
+ "user"
+ ]
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "board"
+ },
+ "boardId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255
+ }
+ },
+ "required": [
+ "type",
+ "boardId"
+ ],
+ "additionalProperties": false
+ }
+ ],
+ "type": "object"
+ }
+ },
+ "right": {
+ "maxItems": 100,
+ "type": "array",
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "builtin"
+ },
+ "id": {
+ "type": "string",
+ "enum": [
+ "logo",
+ "search",
+ "home",
+ "boardSwitcher",
+ "assistant",
+ "docker",
+ "boardEdit",
+ "boardSettings",
+ "settings",
+ "themeToggle",
+ "user"
+ ]
+ }
+ },
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "board"
+ },
+ "boardId": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255
+ }
+ },
+ "required": [
+ "type",
+ "boardId"
+ ],
+ "additionalProperties": false
+ }
+ ],
+ "type": "object"
+ }
+ }
+ },
+ "required": [
+ "left",
+ "center",
+ "right"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "version",
+ "visible",
+ "searchDisplay",
+ "logoDisplay",
+ "zones"
+ ],
+ "additionalProperties": false
+ },
+ "userId": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "colorScheme",
+ "byteUnitSystem",
+ "firstDayOfWeek",
+ "pingIconsEnabled",
+ "enableRightClickOnWidgets",
+ "homeBoardId",
+ "mobileHomeBoardId",
+ "defaultSearchEngineId",
+ "openSearchInNewTab",
+ "ddgBangs",
+ "headerPreferences",
+ "userId"
+ ],
+ "additionalProperties": false,
+ "example": {
+ "userId": "example-user",
+ "colorScheme": "light",
+ "byteUnitSystem": "binary",
+ "firstDayOfWeek": 0,
+ "pingIconsEnabled": true,
+ "enableRightClickOnWidgets": true,
+ "homeBoardId": null,
+ "mobileHomeBoardId": null,
+ "defaultSearchEngineId": null,
+ "openSearchInNewTab": true,
+ "ddgBangs": true,
+ "headerPreferences": {
+ "version": 3,
+ "visible": true,
+ "searchDisplay": "input",
+ "logoDisplay": "logoAndText",
+ "zones": {
+ "left": [
+ {
+ "type": "builtin",
+ "id": "logo"
+ }
+ ],
+ "center": [
+ {
+ "type": "builtin",
+ "id": "search"
+ }
+ ],
+ "right": [
+ {
+ "type": "builtin",
+ "id": "boardEdit"
+ },
+ {
+ "type": "builtin",
+ "id": "boardSettings"
+ },
+ {
+ "type": "builtin",
+ "id": "user"
+ }
+ ]
+ }
+ }
+ }
+ },
"__schema1": {
"anyOf": [
{
diff --git a/apps/docs/src/app/api-reference/page.tsx b/apps/docs/src/app/api-reference/page.tsx
index d7f0386d4..8a019d09b 100644
--- a/apps/docs/src/app/api-reference/page.tsx
+++ b/apps/docs/src/app/api-reference/page.tsx
@@ -13,7 +13,7 @@ export const metadata = pageMetadata({
});
export default function ApiReferenceIndexPage() {
- const pages = apiSource.getPages();
+ const groups = apiSource.pageTree.children.filter((node) => node.type === "folder");
return (