{"openapi":"3.1.0","info":{"title":"Alpsana dashboard editing API","version":"1.0.0","description":"## External dashboard editing workflow\n\nBrowser agents start at the HTML Swagger page at docs/. Select operations, schemas,\nexamples and guidance there to read them in-page. HTTP clients can start with GET\ndocs/agent, resolving its links relative to the guide URL; operation paths are\nrelative to its apiBase link. Avoid loading the full reference unless needed.\nBrowser agents: read the gui reference before using GUI controls (theme, sidebar,\ntime range, save, XS review); do not guess menu locations.\n\nChoose the mode by what you can use:\n- **Browser mode (preferred)**: you control a browser with the dashboard open.\n  Read and apply the draft through **Edit JSON**, review visually, then Save.\n- **API mode**: you have only HTTP access with a personal access token (or\n  AUTH_MODE=none). GET the saved dashboard, edit the JSON locally, then PUT the\n  complete document back. This persists immediately and records a revision.\n\n1. Browser mode: open the existing dashboard in edit mode and read **Edit JSON**\n   for the current unsaved draft; REST reads contain saved state only, and browser\n   variable selections and the active time range are browser-owned. API mode:\n   GET dashboards/{id}; the saved document is the starting point.\n2. Fetch context only when needed: operation details before unfamiliar HTTP calls,\n   schemas for unfamiliar fields, and individual visualization specs for types you\n   edit. Use the visualization summary list only to choose a type. Fetch source\n   metadata only for source/SQL changes. For examples, list dashboard summaries,\n   then read selected dashboards; do not fetch all dashboard content or all specs.\n   Filtered OpenAPI omits examples; add examples=true only when an example helps.\n   Use manifest IDs as visualizationType. Prefer existing visualizations;\n   use custom-echarts only as a last resort.\n3. Preserve unrelated configuration and existing local panel/group/variable IDs.\n   Do not invent source or visualization IDs. Preserve nested visualizationSettings\n   and variable values. Retrieve only relevant schemas such as GridLayout or XsSizing.\n   When file tools are available, edit downloaded JSON programmatically and upload\n   the complete result; return only relevant excerpts from file tools rather than\n   repeatedly printing or generating the whole dashboard.\n4. For SQL changes, read the sql reference and relevant query operation. Supply\n   explicit variables keyed by NAME and resolved datetimeRange.from/to; browser\n   defaults are not inherited. Check error even with HTTP 200. Bound exploratory\n   SELECT results with a small LIMIT and return only useful samples to model context.\n5. Preserve user-requested time ranges, even when empty, and report missing data.\n   Otherwise inspect source timestamps and verify a useful range before choosing\n   defaultDatetimeRange when relevant. Report absent data rather than inventing it.\n6. Browser mode: apply the COMPLETE edited JSON through Edit JSON on the same page.\n   Apply replaces the editable draft, preserving identity/folder/server metadata,\n   and refreshes without persisting. Inspect changed panels and overall layout with\n   your own browser/screenshots; review XS layout by narrowing the viewport below\n   768px. **Save**, then **Save dashboard** in its popup, persists into the same\n   dashboard when requested. Cancel and validation errors leave the draft unchanged.\n   API mode: PUT dashboards/{id} with the complete edited document and a required short\n   saveMessage stating the intention and what changed. Read-only fields from the GET\n   response are ignored. A 400 reports a structure problem (arrays, required\n   variable values, layout overlap/bounds/unknown ids); fix it and PUT again.\n   Verify changed SQL with the dashboard query operation; there is no screenshot\n   API, so report that visual review was not possible.\n   Revisions can be listed and reverted if a save must be undone.\n\nWorkspace resources require auth: send Authorization: Bearer <personal access\ntoken>, or an OIDC access token in OIDC mode; browser sign-in does not authenticate\na separate HTTP client. Read the auth reference\nwhen needed. Reuse fetched references during the task instead of loading them again.\n\n## SQL interpolation and query checks\n\nTest changed SQL with supplied queries, explicit variables keyed by NAME and\nresolved datetimeRange.from/to through dashboard query. Source query uses sql\nand an empty variables map. Neither inherits browser defaults. Both execute\ndriver-supported SQL: no read-only guarantee or source-schema introspection\nendpoint exists. Use supported SQL to inspect sources. Check error with HTTP 200.\n\nSupported: ${name}, ${from}, ${to}, $__timeFrom(), $__timeTo(), and\n$__timeFilter(expression). Values become SQL literals: quoted/escaped strings,\nTRUE/FALSE booleans, comma-separated arrays (use IN (...)), NULL for missing/null/\nempty values. from/to override same-named variables. The time filter expands to\nexpression BETWEEN from AND to. Do not quote placeholders. This is trusted-user\nliteral interpolation, not parameter binding. Panel-source reuse and JavaScript\ntransforms run in the browser; REST returns raw SQL frames. REST results have no\ninternal AI 25-row/12,000-character preview cap.\n\nKeep exploration bounded: use a small LIMIT for sample SELECTs, aggregations for\ncounts/ranges, and select only needed columns. If your client can process responses\noutside model context, return field metadata, errors and a small sample rather than\ndumping complete frames. Do not alter the final panel query merely to cap a preview.\n\n## Authentication and persistence\n\nPersonal access tokens: a user creates one on the **Access tokens** page (sidebar)\nand gives it to the client. Send Authorization: Bearer alpsana_pat_... in every auth\nmode. The token acts as its owner, limited by its optional role cap; saves and\nrevisions are attributed to the owner. Tokens cannot create or revoke tokens.\nAUTH_MODE=none without a token uses the local workspace principal. In OIDC mode an\nOIDC access token from the configured flow also works. Browser sign-in does not\ncookie-authenticate a separate HTTP client. Permissions are listed per operation.\nDocumentation is public; workspace resources/catalog require auth.\nThere is no live external session, screenshot API or optimistic save locking.\nPOST dashboards creates a NEW persisted dashboard (list-page import-as-new).\nPUT dashboards/{id} replaces each supplied top-level key whole (send complete\narrays); omitted keys stay unchanged. It runs light structure checks (400 on\nfailure) and persists immediately with a revision. Prefer\nbrowser Edit JSON then Save when a browser is available; otherwise PUT the complete\nedited document.\n\n## Dashboard GUI map\n\nTop bar: left has the sidebar toggle (\"Open sidebar\"/\"Close sidebar\") and the\nAlpsana logo; right has the user menu (name/role; Logout only in OIDC). The top bar\nhas no theme or settings controls.\n\nLeft sidebar: Dashboards, Shared links, Data sources, Visualizations, Access tokens,\nAdministration\n(permission-dependent) and, at its bottom, the \"Theme\" light/dark switch. The\nsidebar may be collapsed; open it with the top-bar toggle. Below 1024px it is a drawer.\n\nDashboard: view mode is /d/{id}, edit mode /d/{id}/edit. One toolbar card holds\n\"Time: ...\" (presets, From/To, Apply), \"Refresh all\" and \"Auto refresh interval\".\nEdit mode adds \"Add\" (Panel/Group), \"Settings\", \"Edit JSON\" (its book\nicon [data-agent-docs] links to this reference), \"Export dashboard JSON\", \"Save\" and\n\"View mode\". \"Edit with AI\" is the built-in assistant; do not use it.\nView mode shows \"Share\" and \"Edit mode\".\n\nEdit JSON dialog: textarea \"Dashboard JSON\" (#dashboard-json), \"Upload dashboard\nJSON\", \"Download JSON\", \"Cancel\" and \"Apply JSON\". There is no copy button; read the\ntextarea value. Invalid JSON is reported in #dashboard-json-error.\n\nSave: \"Save\" opens a popup with an optional \"Save message\", \"Save dashboard\" and\n\"Update dashboard defaults\" checkboxes (time range, refresh, variable values) shown\nwhen they differ from saved defaults. There is no discard button; switching to\n\"View mode\" drops the unsaved draft after a warning.\n\nVariable controls sit in a row under the toolbar. In edit mode each panel header has\na \"Panel actions\" menu (top right): Edit, Duplicate, Move, Delete. The panel editor\nreplaces the dashboard; its Apply, Discard and Back change only the draft. Group\nheaders have Expand/Collapse group buttons.\n\nReview: each panel element has data-dashboard-panel-id and data-query-state; wait\nuntil none is pending, loading or refreshing, then check error and empty states.\nXS layout applies automatically below a 768px viewport width; there is no preview\ntoggle. Make edits through Edit JSON; use GUI controls for review, time range and save.\n\n## Copyable editing and review prompt\n\n```text\nEdit the open dashboard to satisfy: <user request>.\nWith a browser, read current unsaved Edit JSON; with only API access and a token,\nGET the saved dashboard instead. Open docs/ for browser-readable references\n(HTTP clients can use GET docs/agent). Fetch only relevant operation, schema and\nvisualization details; do not load the full OpenAPI/catalog or all saved\ndashboards. Fetch source metadata and selected examples only if needed. Preserve\nunrelated fields and IDs. Validate changed SQL with explicit variables and resolved range.\nPrefer programmatic edits to a downloaded JSON file and upload when tools support it.\nPreserve user-requested ranges; otherwise verify a useful range from source timestamps.\nReport missing data and inspect query error fields. With a browser, apply a complete\nDashboardJsonPayload through Edit JSON and review changed panels' data, settings and\nlayout with browser tools, including XS sizing/groups when relevant; read the GUI map\nbefore navigating the GUI. Without a browser, PUT the complete document with a\nsaveMessage (intention + what changed) and fix any 400 errors. Fix issues, then\nreport changes and checks.\nIn browser mode, save the dashboard only when the user requests persistence.\n```\n"},"servers":[{"url":".","description":"API base serving this document"}],"tags":[{"name":"Dashboards","description":"Saved dashboards, folders, versions and explicit persistence."},{"name":"Queries","description":"SQL checks with explicit context. Inspect error with HTTP 200."},{"name":"Context","description":"Workspace-scoped data sources and visualization references."},{"name":"Access tokens","description":"Personal access tokens for HTTP clients and agents."},{"name":"Documentation","description":"Public descriptions only; no workspace data."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Personal access token (alpsana_pat_...) or OIDC access token","description":"Personal access tokens work in every auth mode and act as their owner, limited by an optional role cap. In AUTH_MODE=oidc an OIDC access token also works. AUTH_MODE=none without a token uses the local principal. A browser login does not authenticate a separate client."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"DashboardRevisionSummary":{"type":"object","properties":{"id":{"type":"string"},"entityType":{"type":"string"},"entityId":{"type":"string"},"version":{"type":"number"},"message":{"type":"string"},"createdByUserId":{"type":["string","null"]},"createdByName":{"type":["string","null"]},"createdByEmail":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","version","message","createdAt"],"description":"Saved dashboard version. The newest 30 revisions are kept.","examples":[{"id":"revision-id","entityType":"dashboard","entityId":"dashboard-id","version":3,"message":"Add output panel","createdByUserId":"user-id","createdByName":"Agent","createdByEmail":null,"createdAt":"2026-10-08T10:00:00.000Z"}]},"DashboardRevision":{"type":"object","properties":{"snapshot":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"folderId":{"type":["string","null"]},"content":{"type":"string","description":"Dashboard content as a JSON string."}},"required":[]}},"required":[],"allOf":[{"$ref":"#/components/schemas/DashboardRevisionSummary"}],"description":"Revision with its saved snapshot. snapshot.content is a JSON string."},"RevisionRevert":{"type":"object","properties":{"saveMessage":{"type":"string"}},"required":[]},"DashboardFolder":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"parentId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","parentId"],"examples":[{"id":"folder-id","name":"Production","parentId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}]},"DashboardFolderCreate":{"type":"object","properties":{"name":{"type":"string"},"parentId":{"type":["string","null"]}},"required":["name"]},"AccessToken":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"tokenHint":{"type":"string","description":"Last 4 characters of the token."},"role":{"type":["string","null"],"enum":["owner","admin","editor","viewer",null],"description":"Optional role cap; null uses the owner's workspace role."},"expiresAt":{"type":["string","null"],"format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","tokenHint","role","expiresAt","lastUsedAt","revokedAt","createdAt"],"description":"Personal access token metadata. The token value is never returned again.","examples":[{"id":"token-id","name":"Dashboard agent","tokenHint":"x9Qa","role":"editor","expiresAt":null,"lastUsedAt":null,"revokedAt":null,"createdAt":"2026-10-08T10:00:00.000Z"}]},"AccessTokenCreate":{"type":"object","properties":{"name":{"type":"string"},"role":{"type":["string","null"],"enum":["owner","admin","editor","viewer",null],"description":"Optional cap; must not exceed the caller's role."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Optional future expiry."}},"required":["name"]},"CreatedAccessToken":{"allOf":[{"$ref":"#/components/schemas/AccessToken"}],"type":"object","properties":{"token":{"type":"string","description":"Plaintext token, returned only once. Send as Authorization: Bearer <token>."}},"required":["token"]},"DashboardJsonPayload":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"defaultDatetimeRange":{"$ref":"#/components/schemas/DashboardDateTimeRange"},"autoRefreshMs":{"type":"number","minimum":0,"description":"0 disables refresh."},"autoRefreshIntervalsMs":{"type":"array","items":{"type":"number","exclusiveMinimum":0}},"layout":{"$ref":"#/components/schemas/GridLayout"},"variables":{"type":"array","items":{"$ref":"#/components/schemas/DashboardVariable"}},"panelGroups":{"type":"array","items":{"$ref":"#/components/schemas/PanelGroup"}},"panels":{"type":"array","items":{"$ref":"#/components/schemas/Panel"}}},"required":["name","description","defaultDatetimeRange","autoRefreshMs","autoRefreshIntervalsMs","layout","variables","panelGroups","panels"],"description":"Complete editable browser replacement payload. No schemaVersion. Edit JSON projects REST metadata away and replaces these fields/arrays, preserving the current dashboard identity, folder and timestamps. The frontend reconciles layout and normalizes range/refresh defaults. Variable defaultValue, currentValue and options must be present; their values may be arbitrary JSON, including null.","examples":[{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}]}]},"DashboardSave":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"defaultDatetimeRange":{"$ref":"#/components/schemas/DashboardDateTimeRange"},"autoRefreshMs":{"type":"number","minimum":0,"description":"0 disables refresh."},"autoRefreshIntervalsMs":{"type":"array","items":{"type":"number","exclusiveMinimum":0}},"layout":{"$ref":"#/components/schemas/GridLayout"},"variables":{"type":"array","items":{"$ref":"#/components/schemas/DashboardVariable"}},"panelGroups":{"type":"array","items":{"$ref":"#/components/schemas/PanelGroup"}},"panels":{"type":"array","items":{"$ref":"#/components/schemas/Panel"}},"folderId":{"type":["string","null"]},"saveMessage":{"type":"string","description":"Revision note: why the change was made and a short description of what changed. Required for access-token clients."}},"required":[],"description":"Flat REST payload. PUT replaces each supplied top-level key whole (send complete arrays); omitted keys stay unchanged. POST creates a new dashboard. Read-only fields from a GET response (id, workspaceId, folder, createdAt, updatedAt) are ignored, so a fetched document can be sent back whole. The server checks only light structure (400 on failure): panels, panelGroups and variables are arrays, variables have defaultValue, currentValue and options, and layouts reference existing items once, stay within 12 columns and do not overlap.","examples":[{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}]},{"name":"Renamed dashboard","saveMessage":"Match team naming: rename dashboard to Renamed dashboard"}]},"Dashboard":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"defaultDatetimeRange":{"$ref":"#/components/schemas/DashboardDateTimeRange"},"autoRefreshMs":{"type":"number","minimum":0,"description":"0 disables refresh."},"autoRefreshIntervalsMs":{"type":"array","items":{"type":"number","exclusiveMinimum":0}},"layout":{"$ref":"#/components/schemas/GridLayout"},"variables":{"type":"array","items":{"$ref":"#/components/schemas/DashboardVariable"}},"panelGroups":{"type":"array","items":{"$ref":"#/components/schemas/PanelGroup"}},"panels":{"type":"array","items":{"$ref":"#/components/schemas/Panel"}},"id":{"type":"string"},"folderId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["name","description","defaultDatetimeRange","autoRefreshMs","autoRefreshIntervalsMs","layout","variables","panelGroups","panels","id","folderId","createdAt","updatedAt"],"description":"Saved dashboard only; excludes current unsaved browser edits.","examples":[{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}],"id":"dashboard-id","folderId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}]},"DashboardSummary":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"folderId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","description","folderId","createdAt","updatedAt"]},"DashboardDateTimeRange":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":[],"description":"Use explicit from/to bounds: ISO timestamp strings or supported browser relative/date-math expressions (for example now-24h, now or now/d). Storage is permissive; saved ranges may omit bounds. The browser falls back to now-24h to now for missing, invalid or reversed bounds. Query requests require resolved from/to timestamps, not raw relative bounds.","examples":[{"from":"2026-10-07T10:00:00Z","to":"2026-10-08T10:00:00Z"},{"from":"now-24h","to":"now"}]},"ResolvedDateTimeRange":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},"GridLayout":{"type":"array","items":{"$ref":"#/components/schemas/GridLayoutItem"},"description":"Canonical desktop 12-column layout. i is a panel or group ID (groups use their local ID). Group layout contains its own panels. Each i must reference an item in that grid and appear at most once; x/y/w/h are integers with x + w <= 12 and items must not overlap. Frontend places missing layout entries. XS is derived, not saved."},"GridLayoutItem":{"type":"object","properties":{"i":{"type":"string"},"x":{"type":"integer","minimum":0},"y":{"type":"integer","minimum":0},"w":{"type":"integer","minimum":1,"maximum":12},"h":{"type":"integer","minimum":1},"minW":{"type":"number"},"minH":{"type":"number"}},"required":["i","x","y","w","h"]},"XsSizing":{"type":"object","properties":{"minWidth":{"type":"string","enum":["small","medium","large"]},"minHeight":{"type":"string","enum":["small","grid"]}},"required":[],"description":"XS responsive layout (dashboard width below 768px), including panels inside groups.\n\nWidth: panel xsSizing value > visualization xsSizing preference > large. Height: panel xsSizing small or grid > visualization xsSizing small > saved grid height.\n\nSet xsSizing on a panel, not in visualizationSettings or layout.\n\nVisualization definitions may declare the same optional xsSizing object as literal preferences.\n\nOmit a panel field in xsSizing to inherit; set large explicitly to override a compact visualization width preference.\n\nHeight accepts small for a compact panel or grid to retain the saved layout height. Omit it to inherit the visualization preference.\n\nWidth is a minimum; packing may expand it and the container caps it.\n\nCanonical desktop layout is unchanged. Group containers retain full width and content-based sizing.\n\nBuilt-in stat/text prefer small on both axes; other built-ins use large width and saved grid height.\n\nFull shared reference:\n```json\n{\n  \"scope\": \"XS responsive layout (dashboard width below 768px), including panels inside groups.\",\n  \"fields\": {\n    \"xsSizing\": {\n      \"type\": \"object\",\n      \"fields\": {\n        \"minWidth\": {\n          \"type\": \"\\\"small\\\" | \\\"medium\\\" | \\\"large\\\"\",\n          \"fallback\": \"large\"\n        },\n        \"minHeight\": {\n          \"type\": \"\\\"small\\\" | \\\"grid\\\"\",\n          \"fallback\": \"visualization preference, then saved grid height\"\n        }\n      }\n    }\n  },\n  \"precedence\": \"Width: panel xsSizing value > visualization xsSizing preference > large. Height: panel xsSizing small or grid > visualization xsSizing small > saved grid height.\",\n  \"notes\": [\n    \"Set xsSizing on a panel, not in visualizationSettings or layout.\",\n    \"Visualization definitions may declare the same optional xsSizing object as literal preferences.\",\n    \"Omit a panel field in xsSizing to inherit; set large explicitly to override a compact visualization width preference.\",\n    \"Height accepts small for a compact panel or grid to retain the saved layout height. Omit it to inherit the visualization preference.\",\n    \"Width is a minimum; packing may expand it and the container caps it.\",\n    \"Canonical desktop layout is unchanged. Group containers retain full width and content-based sizing.\",\n    \"Built-in stat/text prefer small on both axes; other built-ins use large width and saved grid height.\"\n  ],\n  \"panelExamples\": [\n    {\n      \"xsSizing\": {\n        \"minWidth\": \"medium\",\n        \"minHeight\": \"small\"\n      }\n    },\n    {\n      \"xsSizing\": {\n        \"minWidth\": \"large\",\n        \"minHeight\": \"grid\"\n      }\n    }\n  ]\n}\n```","examples":[{"minWidth":"medium","minHeight":"small"},{"minWidth":"large","minHeight":"grid"}]},"Panel":{"type":"object","properties":{"id":{"type":"string","description":"Dashboard-local ID, unique across all panels."},"panelGroupId":{"type":["string","null"]},"title":{"type":"string"},"description":{"type":"string"},"visualizationType":{"type":"string","description":"Catalog manifest ID, not plugin record ID."},"queries":{"type":"array","items":{"$ref":"#/components/schemas/PanelQuery"}},"transformCode":{"type":"string","description":"Browser JavaScript transform. Empty disables it. Define function transform(dataFrames) returning an array of DataFrame objects. Runs after query execution; REST queries return raw frames.","examples":["function transform(dataFrames) { return dataFrames; }"]},"visualizationSettings":{"type":"object","additionalProperties":true},"xsSizing":{"$ref":"#/components/schemas/XsSizing"}},"required":["id","panelGroupId","title","description","visualizationType","queries","transformCode","visualizationSettings"]},"PanelQuery":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"enabled":{"type":"boolean"},"dataSourceId":{"type":["string","null"]},"query":{"oneOf":[{"$ref":"#/components/schemas/SqlQuery"},{"$ref":"#/components/schemas/PanelSourceQuery"}]}},"required":["id","dataSourceId","query"]},"SqlQuery":{"type":"object","properties":{"sql":{"type":"string"}},"required":["sql"]},"PanelSourceQuery":{"type":"object","properties":{"source":{"type":"string","const":"panel"},"panelId":{"type":"string"}},"required":["source","panelId"],"description":"Frontend-only reuse of another panel's transformed data frames. Must reference an existing panel and be acyclic. The REST SQL executor does not execute panel-source definitions."},"PanelGroup":{"type":"object","properties":{"id":{"type":"string"},"repeatVariableId":{"type":["string","null"]},"collapsed":{"type":"boolean"},"title":{"type":"string"},"description":{"type":"string"},"layout":{"$ref":"#/components/schemas/GridLayout"},"panels":{"type":"array","items":{"$ref":"#/components/schemas/Panel"}}},"required":["id","repeatVariableId","title","description","layout","panels"],"description":"Dashboard-local group. Child panelGroupId equals this id. repeatVariableId references a dashboard variable's id; repetition is browser-derived from selected values and does not create saved panels."},"DashboardVariable":{"type":"object","properties":{"id":{"type":"string"},"dataSourceId":{"type":["string","null"]},"name":{"type":"string"},"label":{"type":"string"},"type":{"type":"string","enum":["select","multi-select"]},"hidden":{"type":"boolean"},"defaultValue":{},"currentValue":{},"options":{"description":"JSON values; normally an array of label/value options. The frontend also normalizes primitive options."},"query":{"$ref":"#/components/schemas/SqlQuery"},"valueField":{"type":"string"},"labelField":{"type":"string"},"includeAll":{"type":"boolean"},"allLabel":{"type":"string"},"sortMode":{"type":"string","enum":["none","alphanumeric","natural"]},"sortDirection":{"type":"string","enum":["asc","desc"]},"refreshOn":{"type":"string","enum":["time-range-change","dashboard-open"]},"isQueryBacked":{"type":"boolean"}},"required":["id","dataSourceId","name","label","type","defaultValue","currentValue","options","query","valueField","labelField","includeAll","allLabel","sortMode","sortDirection"]},"VariableOption":{"type":"object","properties":{"label":{"type":"string"},"value":{}},"required":["label","value"]},"DashboardQueryRequest":{"type":"object","properties":{"panelId":{"type":"string"},"id":{"type":"string"},"queries":{"type":"array","items":{"$ref":"#/components/schemas/PanelQuery"}},"variables":{"type":"object","additionalProperties":true},"datetimeRange":{"$ref":"#/components/schemas/ResolvedDateTimeRange"},"includeExecutedQuery":{"type":"boolean"},"mode":{"type":"string","enum":["view","edit"]},"trigger":{"type":"string","enum":["load","reload","refresh","auto-refresh","time-range","variable","mode-change","dashboard-change","panel-refresh","view-data","edit","edit-preview"]}},"required":["queries","datetimeRange"],"description":"Executes supplied SQL query definitions (including unsaved SQL), not saved definitions looked up by panelId. Disabled queries are skipped. variables is keyed by variable NAME. No browser defaults or AI preview cap.","examples":[{"panelId":"panel_1","queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}],"variables":{"machine":["A","B"]},"datetimeRange":{"from":"2026-10-07T10:00:00Z","to":"2026-10-08T10:00:00Z"},"includeExecutedQuery":true}]},"SourceQueryRequest":{"type":"object","properties":{"sql":{"type":"string"},"datetimeRange":{"$ref":"#/components/schemas/ResolvedDateTimeRange"}},"required":["sql","datetimeRange"],"description":"Source query uses an empty variable map. Supply explicit range; use dashboard query when testing named variable interpolation.","examples":[{"sql":"SELECT 42 AS value","datetimeRange":{"from":"2026-10-07T10:00:00Z","to":"2026-10-08T10:00:00Z"}}]},"DataFrameField":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["time","number","string","boolean","other"]},"values":{"type":"array","items":{}}},"required":["name","type","values"]},"DataFrame":{"type":"object","properties":{"id":{"type":"string"},"dataSourceId":{"type":["string","null"]},"fields":{"type":"array","items":{"$ref":"#/components/schemas/DataFrameField"}},"length":{"type":"number"},"metadata":{"type":"object","properties":{"durationMs":{"type":"number"},"rowCount":{"type":"number"}},"required":["durationMs","rowCount"]}},"required":["id","dataSourceId","fields","length","metadata"]},"PanelExecutionResult":{"type":"object","properties":{"dataFrames":{"type":"array","items":{"$ref":"#/components/schemas/DataFrame"}},"metadata":{"type":"object","properties":{"durationMs":{"type":"number"},"rowCount":{"type":"number"},"frameCount":{"type":"number"}},"required":["durationMs","rowCount","frameCount"]},"executedQueries":{"type":"array","items":{"type":"object","properties":{"queryId":{"type":"string"},"query":{"type":"string"}},"required":["queryId","query"]}},"error":{"type":"string"}},"required":["dataFrames","metadata"],"description":"Always inspect error even with HTTP 200. Raw column-oriented data frames; JavaScript transforms run separately in the browser. REST responses are not subject to internal AI preview truncation.","examples":[{"dataFrames":[{"id":"query_1","dataSourceId":"source-id","length":1,"fields":[{"name":"time","type":"time","values":["2026-10-08T09:00:00Z"]},{"name":"value","type":"number","values":[42]}],"metadata":{"durationMs":2,"rowCount":1}}],"metadata":{"durationMs":2,"rowCount":1,"frameCount":1}},{"dataFrames":[],"metadata":{"durationMs":2,"rowCount":0,"frameCount":0},"error":"no such table: output"}]},"DataSource":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"config":{"type":"object","additionalProperties":true},"workspaceId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","type","config","createdAt","updatedAt"],"description":"Workspace-scoped metadata. Config is serialized with passwords removed; PostgreSQL exposes passwordSet, never the password.","examples":[{"id":"source-id","name":"Factory","type":"sqlite","config":{"filename":"factory.db"},"workspaceId":"default_workspace","createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}]},"VisualizationSettingsField":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string","enum":["string","number","boolean","array","object"]},"description":{"type":"string"},"defaultValue":{},"required":{"type":"boolean"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/VisualizationSettingsField"}}},"required":["key","type"]},"VisualizationSettingsSchema":{"type":"object","properties":{"version":{"type":"number","const":1},"fields":{"type":"array","items":{"$ref":"#/components/schemas/VisualizationSettingsField"}}},"required":["version","fields"],"description":"UI field descriptions used by built-ins. This is not JSON Schema. Custom plugin settingsSchema remains opaque JSON and may use another format."},"VisualizationSummary":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}},"required":["id","name","description"]},"VisualizationSpec":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"expectedDataShape":{"type":"string"},"defaultSettings":{"type":"object","additionalProperties":true},"xsSizing":{"$ref":"#/components/schemas/XsSizing"},"configurableOptions":{"type":"array","items":{"type":"string"}},"settingsSchema":{"description":"Optional JSON metadata. Built-in field format is described by VisualizationSettingsSchema; custom values are opaque."}},"required":["id","name","description","expectedDataShape","defaultSettings","configurableOptions"]},"VisualizationPluginManifest":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"expectedDataShape":{"type":"string"},"defaultSettings":{"type":"object","additionalProperties":true},"xsSizing":{"$ref":"#/components/schemas/XsSizing"},"settingsSchema":{}},"required":["id","name","description","expectedDataShape","defaultSettings"]},"VisualizationPlugin":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"manifest":{"$ref":"#/components/schemas/VisualizationPluginManifest"},"enabled":{"type":"boolean"},"compiledHash":{"type":"string"},"moduleUrl":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","name","description","manifest","enabled","compiledHash","moduleUrl","createdAt","updatedAt"]},"VisualizationPluginWithSource":{"allOf":[{"$ref":"#/components/schemas/VisualizationPlugin"},{"type":"object","properties":{"sourceCode":{"type":"string"}},"required":["sourceCode"]}]},"VisualizationPluginCreate":{"type":"object","properties":{"sourceCode":{"type":"string","description":"TypeScript/TSX module whose default export defines the plugin."},"id":{"type":"string","description":"Optional; must match the manifest id in sourceCode."},"enabled":{"type":"boolean"},"saveMessage":{"type":"string"}},"required":["sourceCode"]},"VisualizationPluginUpdate":{"type":"object","properties":{"sourceCode":{"type":"string","description":"Replaces and recompiles the source; the manifest id must not change."},"enabled":{"type":"boolean"},"saveMessage":{"type":"string"}},"required":[]}}},"paths":{"/dashboards":{"get":{"operationId":"listDashboards","summary":"List saved dashboard examples","description":"Returns summaries by default, or complete saved documents when includeContent=true. Ordered by updatedAt descending. No unsaved state.\n\nPermission: `dashboards:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"parameters":[{"name":"includeContent","in":"query","required":false,"description":"Return complete saved dashboard content when true.","schema":{"type":"boolean","default":false},"example":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/DashboardSummary"}},{"type":"array","items":{"$ref":"#/components/schemas/Dashboard"}}]},"example":[{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}],"id":"dashboard-id","folderId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}]}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}},"post":{"operationId":"createDashboard","summary":"Create/import a new dashboard","description":"Creates and persists a NEW dashboard. folderId must belong to this workspace. Light structure checks return 400 (see DashboardSave).\n\nPermission: `dashboards:create`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardSave"},"example":{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}]}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dashboard"},"example":{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}],"id":"dashboard-id","folderId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/dashboards/{id}":{"get":{"operationId":"getDashboard","summary":"Read a saved dashboard","description":"Full saved document with server metadata. In a browser session the Edit JSON dialog holds the current unsaved draft instead.\n\nPermission: `dashboards:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dashboard"},"example":{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}],"id":"dashboard-id","folderId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}},"put":{"operationId":"saveDashboard","summary":"Persist dashboard changes","description":"Immediately persists a flat payload and records a revision (saveMessage, required for access-token clients). Supplied top-level keys are replaced whole; omitted keys stay unchanged. Token-only clients send the complete edited GET response. Light structure checks return 400. No optimistic locking. Browser Apply JSON is a separate unsaved replacement action.\n\nPermission: `dashboards:update`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"}],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardSave"},"example":{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dashboard"},"example":{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}],"id":"dashboard-id","folderId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}},"delete":{"operationId":"deleteDashboard","summary":"Delete a dashboard","description":"Deletes the dashboard, its shares and its revisions. Cannot be undone.\n\nPermission: `dashboards:delete`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Success, no content"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/dashboards/{id}/revisions":{"get":{"operationId":"listDashboardRevisions","summary":"List saved dashboard versions","description":"Newest first. Every create, save and revert adds a revision.\n\nPermission: `dashboards:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DashboardRevisionSummary"}},"example":[{"id":"revision-id","entityType":"dashboard","entityId":"dashboard-id","version":3,"message":"Add output panel","createdByUserId":"user-id","createdByName":"Agent","createdByEmail":null,"createdAt":"2026-10-08T10:00:00.000Z"}]}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/dashboards/{id}/revisions/{revisionId}":{"get":{"operationId":"getDashboardRevision","summary":"Read a dashboard version","description":"Revision metadata with the saved snapshot.\n\nPermission: `dashboards:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"},{"name":"revisionId","in":"path","required":true,"description":"Revision ID from the dashboard revision list.","schema":{"type":"string"},"example":"revision-id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardRevision"},"example":{"id":"revision-id","entityType":"dashboard","entityId":"dashboard-id","version":3,"message":"Add output panel","createdByUserId":"user-id","createdByName":"Agent","createdByEmail":null,"createdAt":"2026-10-08T10:00:00.000Z","snapshot":{"name":"Production overview","description":"Hourly output","folderId":null,"content":"{...}"}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/dashboards/{id}/revisions/{revisionId}/revert":{"post":{"operationId":"revertDashboardRevision","summary":"Restore a dashboard version","description":"Restores the snapshot and records it as a new revision.\n\nPermission: `dashboards:update`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"},{"name":"revisionId","in":"path","required":true,"description":"Revision ID from the dashboard revision list.","schema":{"type":"string"},"example":"revision-id"}],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevisionRevert"},"example":{"saveMessage":"Undo agent edit"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Dashboard"},"example":{"name":"Production overview","description":"Hourly output","defaultDatetimeRange":{"from":"now-24h","to":"now"},"autoRefreshMs":0,"autoRefreshIntervalsMs":[10000,30000,60000],"layout":[{"i":"panel_1","x":0,"y":0,"w":12,"h":6}],"variables":[],"panelGroups":[],"panels":[{"id":"panel_1","panelGroupId":null,"title":"Output","description":"","visualizationType":"line","transformCode":"","xsSizing":{"minWidth":"large"},"visualizationSettings":{"xKey":"time","yKey":"value"},"queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}]}],"id":"dashboard-id","folderId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/dashboards/{id}/query":{"post":{"operationId":"executeDashboardQuery","summary":"Test supplied dashboard SQL","description":"Executes supplied queries against workspace sources with explicit variables/range. panelId identifies context, not a saved query lookup. Optional mode/trigger only label usage telemetry. Panel-source definitions are frontend-only. Check result.error even with HTTP 200.\n\nPermission: `dashboards:query`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Queries"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"}],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardQueryRequest"},"example":{"panelId":"panel_1","queries":[{"id":"query_1","dataSourceId":"source-id","query":{"sql":"SELECT time, value FROM output WHERE $__timeFilter(time)"}}],"variables":{"machine":["A","B"]},"datetimeRange":{"from":"2026-10-07T10:00:00Z","to":"2026-10-08T10:00:00Z"},"includeExecutedQuery":true}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PanelExecutionResult"},"example":{"dataFrames":[{"id":"query_1","dataSourceId":"source-id","length":1,"fields":[{"name":"time","type":"time","values":["2026-10-08T09:00:00Z"]},{"name":"value","type":"number","values":[42]}],"metadata":{"durationMs":2,"rowCount":1}}],"metadata":{"durationMs":2,"rowCount":1,"frameCount":1}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/dashboard-folders":{"get":{"operationId":"listDashboardFolders","summary":"List dashboard folders","description":"Flat folder list; parentId builds the hierarchy. Use an id as dashboard folderId.\n\nPermission: `dashboard_folders:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DashboardFolder"}},"example":[{"id":"folder-id","name":"Production","parentId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}]}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}},"post":{"operationId":"createDashboardFolder","summary":"Create a dashboard folder","description":"Sibling names must be unique (409).\n\nPermission: `dashboard_folders:create`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Dashboards"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardFolderCreate"},"example":{"name":"Production","parentId":null}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardFolder"},"example":{"id":"folder-id","name":"Production","parentId":null,"createdAt":"2026-10-08T10:00:00.000Z","updatedAt":"2026-10-08T10:00:00.000Z"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/access-tokens":{"get":{"operationId":"listAccessTokens","summary":"List my personal access tokens","description":"Token metadata for the caller only; token values are never returned.\n\nPermission: `authenticated user`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Access tokens"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AccessToken"}},"example":[{"id":"token-id","name":"Dashboard agent","tokenHint":"x9Qa","role":"editor","expiresAt":null,"lastUsedAt":null,"revokedAt":null,"createdAt":"2026-10-08T10:00:00.000Z"}]}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}},"post":{"operationId":"createAccessToken","summary":"Create a personal access token","description":"Returns the plaintext token once. The token acts as the caller, limited by the optional role cap. Requires an interactive (browser/OIDC or AUTH_MODE=none) session; requests authenticated with an access token get 403.\n\nPermission: `authenticated user`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Access tokens"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessTokenCreate"},"example":{"name":"Dashboard agent","role":"editor","expiresAt":null}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedAccessToken"},"example":{"id":"token-id","name":"Dashboard agent","tokenHint":"x9Qa","role":"editor","expiresAt":null,"lastUsedAt":null,"revokedAt":null,"createdAt":"2026-10-08T10:00:00.000Z","token":"alpsana_pat_..."}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/access-tokens/{id}":{"delete":{"operationId":"revokeAccessToken","summary":"Revoke a personal access token","description":"Revoked tokens stop working immediately. Requires an interactive (browser/OIDC or AUTH_MODE=none) session; requests authenticated with an access token get 403.\n\nPermission: `authenticated user`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Access tokens"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"dashboard-id"}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Success, no content"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/data-sources":{"get":{"operationId":"listDataSources","summary":"List source metadata","description":"Workspace-scoped sources ordered by updatedAt descending. Uses the password-sanitizing serializer. IDs/names/types provide editing context.\n\nPermission: `datasources:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Context"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DataSource"}},"example":[{"id":"source-id","name":"Factory","type":"sqlite","config":{"filename":"factory.db"},"workspaceId":"default_workspace","createdAt":"2026-10-08T10:00:00Z","updatedAt":"2026-10-08T10:00:00Z"}]}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/data-sources/{id}/query":{"post":{"operationId":"executeDataSourceQuery","summary":"Test SQL on a source","description":"Supply sql and resolved datetimeRange. Uses an empty variables map; no browser state inheritance. Supports driver SQL and time interpolation. Check result.error even with HTTP 200. No internal AI preview truncation.\n\nPermission: `datasources:query`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Queries"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"source-id"}],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceQueryRequest"},"example":{"sql":"SELECT 42 AS value","datetimeRange":{"from":"2026-10-07T10:00:00Z","to":"2026-10-08T10:00:00Z"}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PanelExecutionResult"},"example":{"dataFrames":[{"id":"query_1","dataSourceId":"source-id","length":1,"fields":[{"name":"time","type":"time","values":["2026-10-08T09:00:00Z"]},{"name":"value","type":"number","values":[42]}],"metadata":{"durationMs":2,"rowCount":1}}],"metadata":{"durationMs":2,"rowCount":1,"frameCount":1}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/visualization-plugins":{"get":{"operationId":"listVisualizationPlugins","summary":"List custom plugin metadata","description":"Includes enabled AND disabled custom plugins, ordered by updatedAt descending, without source/compiled code. For editing, use the combined catalog of built-ins and enabled plugins. visualizationType is manifest.id.\n\nPermission: `visualization_plugins:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Context"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VisualizationPlugin"}},"example":[{"id":"ai.workspace.metric","name":"Metric","description":"A metric","manifest":{"id":"ai.workspace.metric","name":"Metric","description":"A metric","expectedDataShape":"First row numeric value","defaultSettings":{}},"enabled":true,"compiledHash":"hash","moduleUrl":"/api/visualization-plugins/ai.workspace.metric/module.js?v=hash","createdAt":"2026-10-08T10:00:00Z","updatedAt":"2026-10-08T10:00:00Z"}]}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}},"post":{"operationId":"createVisualizationPlugin","summary":"Create/import a custom plugin","description":"Compiles sourceCode on the server. The id comes from the source manifest; an explicit id must match it. Returns 409 when the id exists. To copy a plugin between instances, read its sourceCode with getVisualizationPlugin and create it here.\n\nPermission: `visualization_plugins:create`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Context"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualizationPluginCreate"},"example":{"sourceCode":"export default { id: \"ai.workspace.metric\", ... };","enabled":true,"saveMessage":"Import plugin"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualizationPlugin"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/visualization-plugins/{id}":{"get":{"operationId":"getVisualizationPlugin","summary":"Read a custom plugin with source","description":"Metadata plus the TypeScript sourceCode, including disabled plugins.\n\nPermission: `visualization_plugins:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Context"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"ai.workspace.metric"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualizationPluginWithSource"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}},"patch":{"operationId":"updateVisualizationPlugin","summary":"Update a custom plugin","description":"Replaces sourceCode (recompiled; manifest id must not change) and/or enabled. Records a revision.\n\nPermission: `visualization_plugins:update`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Context"],"parameters":[{"name":"id","in":"path","required":true,"description":"Existing resource ID in the authenticated workspace.","schema":{"type":"string"},"example":"ai.workspace.metric"}],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualizationPluginUpdate"},"example":{"enabled":false,"saveMessage":"Disable plugin"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualizationPlugin"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/agent/dashboard-editing/visualizations":{"get":{"operationId":"listDashboardEditingVisualizations","summary":"List visualization summaries or editing specifications","description":"Prefer view=summary to choose a type, then read visualizations/{id} only for types you edit. Full view has the same specs as built-in chat: built-ins first, then enabled workspace custom plugins sorted by manifest name. No component functions or plugin source/compiled code. Includes settings, expected data shape and XS sizing preferences. settingsSchema is UI metadata/opaque JSON, not necessarily JSON Schema. Available without a chat session.\n\nPermission: `visualization_plugins:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Context"],"parameters":[{"name":"view","in":"query","schema":{"type":"string","enum":["summary","full"],"default":"full"},"description":"summary returns only id/name/description; full retains the complete catalog."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/VisualizationSummary"}},{"type":"array","items":{"$ref":"#/components/schemas/VisualizationSpec"}}]},"example":[{"id":"line","name":"Line chart","description":"Time series or ordered value chart with numeric series from every data frame.","expectedDataShape":"Data frames with a compatible time or ordered x field and one or more numeric fields. Each frame contributes labeled series.","defaultSettings":{"xKey":"time","yKey":"value","seriesKeys":[],"matchDashboardTimeRange":true,"yMin":null,"yMax":null,"showTimeline":true,"showLegend":true,"colors":[],"line":{"show":true,"width":2.5,"stroke":null,"fill":null,"fillTo":null,"dash":[],"cap":null,"spanGaps":false,"alpha":1},"points":{"show":false,"size":6,"space":null,"width":1,"stroke":"series","fill":"series","dash":[],"cap":null}},"configurableOptions":["xKey","yKey","seriesKeys","matchDashboardTimeRange","yMin","yMax","showTimeline","showLegend","colors","line","points"],"settingsSchema":{"version":1,"fields":[{"key":"xKey","label":"X field","type":"string","defaultValue":"time"},{"key":"yKey","label":"Y field","type":"string","defaultValue":"value"},{"key":"seriesKeys","label":"Series fields","type":"array","defaultValue":[]},{"key":"matchDashboardTimeRange","label":"Match dashboard time range","type":"boolean","defaultValue":true},{"key":"yMin","label":"Y minimum","type":"number","defaultValue":null},{"key":"yMax","label":"Y maximum","type":"number","defaultValue":null},{"key":"showTimeline","label":"Show timeline","type":"boolean","defaultValue":true},{"key":"showLegend","label":"Show legend","type":"boolean","defaultValue":true},{"key":"colors","label":"Colors","type":"array","defaultValue":[]},{"key":"line","label":"Line","type":"object","fields":[{"key":"show","label":"Show line","type":"boolean","defaultValue":true},{"key":"width","label":"Width","type":"number","defaultValue":2.5},{"key":"stroke","label":"Stroke","type":"string","defaultValue":null},{"key":"fill","label":"Fill","type":"string","defaultValue":null},{"key":"fillTo","label":"Fill to","type":"string","defaultValue":null},{"key":"dash","label":"Dash","type":"array","defaultValue":[]},{"key":"cap","label":"Cap","type":"string","defaultValue":null},{"key":"spanGaps","label":"Span gaps","type":"boolean","defaultValue":false},{"key":"alpha","label":"Alpha","type":"number","defaultValue":1}]},{"key":"points","label":"Points","type":"object","fields":[{"key":"show","label":"Show points","type":"boolean","defaultValue":false},{"key":"size","label":"Size","type":"number","defaultValue":6},{"key":"space","label":"Space","type":"number","defaultValue":null},{"key":"width","label":"Width","type":"number","defaultValue":1},{"key":"stroke","label":"Stroke","type":"string","defaultValue":"series"},{"key":"fill","label":"Fill","type":"string","defaultValue":"series"},{"key":"dash","label":"Dash","type":"array","defaultValue":[]},{"key":"cap","label":"Cap","type":"string","defaultValue":null}]}]}}]}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/agent/dashboard-editing/visualizations/{id}":{"get":{"operationId":"getDashboardEditingVisualization","summary":"Read one visualization editing specification","description":"Look up a built-in or enabled workspace plugin by manifest ID, not plugin record ID. Includes settings, expected data shape and XS sizing. No component functions or plugin source/compiled code.\n\nPermission: `visualization_plugins:read`. All resource reads and queries are scoped to the authenticated workspace.","tags":["Context"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Visualization manifest ID."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VisualizationSpec"},"example":{"id":"stat","name":"Stat card","description":"Single important metric with auto-fit sizing, threshold coloring, and an optional compact sparkline.","expectedDataShape":"Each data frame's first row contains a numeric value. Optional time-series rows can include time and valueSeries fields for sparklines.","defaultSettings":{"valueKey":"value","xKey":"time","yKey":"valueSeries","prefix":"","suffix":"","percentage":false,"percentageDecimals":2,"defaultColor":"#0f172a","thresholds":[],"showTrendArrow":false,"showTrendPercentage":false},"xsSizing":{"minWidth":"small","minHeight":"small"},"configurableOptions":["valueKey","xKey","yKey","prefix","suffix","percentage","percentageDecimals","defaultColor","thresholds","showTrendArrow","showTrendPercentage"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Invalid request."}}}},"401":{"description":"Bearer token missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unauthorized."}}}},"403":{"description":"Role lacks the required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Insufficient permissions."}}}},"404":{"description":"Resource absent from this workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Not found."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Unexpected server error."}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"Read this OpenAPI 3.1 document","description":"Full public contract by default. Browser agents start at docs; HTTP clients can use docs/agent and select one operationId or schema. Filtered documents include recursive schema dependencies and omit examples unless examples=true. Full reads keep examples unless examples=false.","tags":["Documentation"],"security":[],"parameters":[{"name":"operationId","in":"query","schema":{"type":"string"},"description":"Select one operation by its operationId. Cannot combine with schema."},{"name":"schema","in":"query","schema":{"type":"string"},"description":"Select one component schema and its dependencies. Cannot combine with operationId."},{"name":"examples","in":"query","schema":{"type":"boolean"},"description":"Include examples: defaults to false for filtered reads, true for full reads."}],"responses":{"200":{"description":"OpenAPI 3.1 specification","content":{"application/json":{"schema":{"type":"object"},"example":{"openapi":"3.1.0","info":{"title":"Alpsana dashboard editing API","version":"1.0.0"},"paths":{}}}}},"400":{"description":"Invalid selectors or examples flag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown operation or schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/docs/agent":{"get":{"operationId":"getAgentDocumentation","summary":"Start external dashboard editing","description":"Compact workflow, operation summaries, schema names and relative links to on-demand references. No workspace data or full schemas/specs.","tags":["Documentation"],"security":[],"responses":{"200":{"description":"Compact agent guide","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/docs/agent/{topic}":{"get":{"operationId":"getAgentReference","summary":"Read one editing reference topic","description":"Fetch only when relevant to the edit. SQL, auth/persistence and the copyable review prompt are separate from the initial guide.","tags":["Documentation"],"security":[],"parameters":[{"name":"topic","in":"path","required":true,"schema":{"type":"string","enum":["sql","auth","gui","review"]}}],"responses":{"200":{"description":"Selected Markdown reference","content":{"text/markdown":{"schema":{"type":"string"}}}},"404":{"description":"Unknown reference topic","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/docs":{"get":{"operationId":"getApiDocumentation","summary":"Open Swagger UI","description":"Public, locally packaged Swagger UI with a compact index. Selected operations, schemas, examples and guidance load in-page on demand. Redirects to trailing slash.","tags":["Documentation"],"security":[],"responses":{"200":{"description":"Swagger UI HTML","content":{"text/html":{"schema":{"type":"string"},"example":"<!doctype html><html>Swagger UI</html>"}}},"302":{"description":"Redirect to documentation directory","headers":{"Location":{"schema":{"type":"string"},"example":"./docs/"}}}}}}}}