diff --git a/docs/sources/developers/http_api/apis.md b/docs/sources/developers/http_api/apis.md new file mode 100644 index 00000000000..c00d34b4de7 --- /dev/null +++ b/docs/sources/developers/http_api/apis.md @@ -0,0 +1,99 @@ +--- +aliases: + - ../../http_api/new_api_structure/ + - ../../http_api/new_api_structure/ +canonical: /docs/grafana/latest/developers/http_api/new_api_structure/ +description: '' +keywords: + - grafana + - http + - documentation + - api +labels: + products: + - enterprise + - oss +title: New API Structure +--- + +# Grafana's New API Structure + +## Overview + +Going forward, Grafana's HTTP API will follow a standardized API structure alongside consistent API versioning. + +## API Path Structure + +All Grafana APIs follow this standardized format: + +``` +/apis///namespaces//[/] +``` + +Where the final `/` segment is used for operations on individual resources (like Get, Update, Delete) and omitted for collection operations (like List, Create). + +## Understanding the Components + +### Group (``) + +Groups organize related functionality into logical collections. For example `dashboard.grafana.app` will be used for all dashboard-related operations. + +### Version (``) + +These APIs will also uses semantic versioning with three stability levels: + +| Level | Format | Description | Use Case | +| ----- | ---------- | --------------------------------------------------------------------------- | ------------------------ | +| Alpha | `v1alpha1` | Early development stage. Unstable, may contain bugs, and subject to removal | For testing new features | +| Beta | `v1beta1` | More stable than alpha, but may still have some changes | For early production use | +| GA | `v1` | Generally Available. Stable with backward compatibility guarantees | For production use | + +### Namespace (``) + +Namespaces isolate resources within your Grafana instance. The format varies by deployment type: + +#### OSS & On-Premise Grafana + +- Default organization (org 1): `default` +- Additional organizations: `org-` + +#### Grafana Cloud + +- Format: `stacks-` +- Your instance ID is the `stack_id`. You can find this value by either: + - Going to grafana.com, clicking on your stack, and selecting "Details" on your Grafana instance + - Accessing the /swagger page in your cloud instance, where the namespace will be automatically populated on the relevant endpoints + +### Resource (``) + +Represents the core resource you want to interact with, such as: + +- `dashboards` +- `playlists` +- `folders` + +### Name () + +The `` is the unique identifier for a specific instance of a resource within its namespace and resource type. `` is distinct from the metadata.uid field. The URL path will always use the metadata.name. + +For example, to get a dashboard defined as: + +``` +{ + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v1beta1", + "metadata": { + "name": "production-overview", // This value IS used in the URL path + "namespace": "default", + "uid": "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8" // This value is NOT used in the URL path + // ... other metadata + }, + "spec": { + // ... dashboard spec + } +} +``` + +You would use the following API call: + +`GET /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/production-overview` diff --git a/docs/sources/developers/http_api/dashboard.md b/docs/sources/developers/http_api/dashboard.md index d3e0a61242f..2dfe6322441 100644 --- a/docs/sources/developers/http_api/dashboard.md +++ b/docs/sources/developers/http_api/dashboard.md @@ -16,22 +16,859 @@ labels: title: Dashboard HTTP API --- -# Dashboard API +# New Dashboard APIs > If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information. -## Identifier (id) vs unique identifier (uid) +> To view more about the new api structure, refer to [API overview]({{< ref "apis" >}}). -The identifier (id) of a dashboard is an auto-incrementing numeric value and is only unique per Grafana install. +## Create Dashboard -The unique identifier (uid) of a dashboard can be used for uniquely identify a dashboard between multiple Grafana installs. +`POST /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards` + +Creates a new dashboard. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). + +**Required permissions** + +See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation. + + +| Action | Scope | +| ------------------- | ------------------------------------------------------------------------------------------------------- | +| `dashboards:create` |
  • `folders:*`
  • `folders:uid:*`
| +| `dashboards:write` |
  • `dashboards:*`
  • `dashboards:uid:*`
  • `folders:*`
  • `folders:uid:*`
| +{ .no-spacing-list } + + +**Example Create Request**: + +```http +POST /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +{ + "metadata": { + "name": "gdxccn", + "annotations": { + "grafana.app/folder": "fef30w4jaxla8b" + }, + }, + "spec": { + "annotations": { + "list": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "Example annotation", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "Example Link", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "description": "With a description", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.0", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Example panel", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": ["example"], + "templating": { + "list": [ + { + "current": { + "text": "", + "value": "" + }, + "definition": "", + "description": "example description", + "label": "ExampleLabel", + "name": "ExampleVariable", + "options": [], + "query": "", + "refresh": 1, + "regex": "cluster", + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Example Dashboard", + "version": 0 + } +} +``` + +JSON Body schema: + +- **metadata.name** – The Grafana [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}). If you do not want to provide this, set metadata.generateName instead to the prefix you would like for the randomly generated uid (cannot be an empty string). +- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the folder under which the dashboard should be created. +- **spec** – The dashboard json. + +**Example Response**: + +```http +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 485 + +{ + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v1beta1", + "metadata": { + "name": "gdxccn", + "namespace": "default", + "uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX", + "resourceVersion": "1", + "generation": 1, + "creationTimestamp": "2025-04-24T20:35:29Z", + "labels": { + "grafana.app/deprecatedInternalID": "11" + }, + "annotations": { + "grafana.app/createdBy": "service-account:dejwtrofg77y8d", + "grafana.app/folder": "fef30w4jaxla8b" + }, + "managedFields": [ + { + "manager": "curl", + "operation": "Update", + "apiVersion": "dashboard.grafana.app/v0alpha1", + "time": "2025-04-24T20:35:29Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:spec": { + "f:annotations": { + ".": {}, + "f:list": {} + }, + "f:editable": {}, + "f:fiscalYearStartMonth": {}, + "f:graphTooltip": {}, + "f:links": {}, + "f:panels": {}, + "f:preload": {}, + "f:schemaVersion": {}, + "f:tags": {}, + "f:templating": { + ".": {}, + "f:list": {} + }, + "f:time": { + ".": {}, + "f:from": {}, + "f:to": {} + }, + "f:timepicker": {}, + "f:timezone": {}, + "f:title": {}, + "f:version": {} + } + } + } + ] + }, + "spec": { + "annotations": { + "list": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "Example annotation", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "Example Link", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "description": "With a description", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.0", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Example panel", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [ + "example" + ], + "templating": { + "list": [ + { + "current": { + "text": "", + "value": "" + }, + "definition": "", + "description": "example description", + "label": "ExampleLabel", + "name": "ExampleVariable", + "options": [], + "query": "", + "refresh": 1, + "regex": "cluster", + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Example Dashboard" + }, + "status": {} +``` + +Status Codes: + +- **201** – Created +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Access denied +- **409** – Conflict (dashboard with the same uid already exists) + +## Update Dashboard + +`PUT /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards/:uid` + +Updates an existing dashboard via the dashboard uid. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). +- uid: the unique identifier of the dashboard to update. this will be the _name_ in the dashboard response + +**Required permissions** + +See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation. + + +| Action | Scope | +| ------------------- | ------------------------------------------------------------------------------------------------------- | +| `dashboards:write` |
  • `dashboards:*`
  • `dashboards:uid:*`
  • `folders:*`
  • `folders:uid:*`
| +{ .no-spacing-list } + + +**Example Update Request**: + +```http +POST /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/gdxccn HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +{ + "metadata": { + "name": "gdxccn", + "annotations": { + "grafana.app/folder": "fef30w4jaxla8b", + "grafana.app/message": "commit message" + }, + }, + "spec": { + "title": "New dashboard - updated", + "schemaVersion": 41, + ... + } +} +``` + +JSON Body schema: + +- **metadata.name** – The [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}). +- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the folder under which the dashboard should be created. +- **metadata.annotations.grafana.app/message** - Optional field, to set a commit message for the version history. +- **spec** – The dashboard json. + +**Example Response**: + +```http +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 485 + +{ + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v1beta1", + "metadata": { + "name": "gdxccn", + "namespace": "default", + "uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX", + "resourceVersion": "2", + "generation": 2, + "creationTimestamp": "2025-03-06T19:57:18Z", + "annotations": { + "grafana.app/folder": "fef30w4jaxla8b", + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedTimestamp": "2025-03-07T02:58:36Z" + } + }, + "spec": { + "schemaVersion": 41, + "title": "New dashboard - updated", + ... + } +} +``` + +Status Codes: + +- **200** – OK +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Access denied +- **409** – Conflict (dashboard with the same version already exists) + +## Get Dashboard + +`GET /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards/:uid` + +Gets a dashboard via the dashboard uid. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). +- uid: the unique identifier of the dashboard to update. this will be the _name_ in the dashboard response + +Note: For large dashboards, add `/dto` to the end of the URL to get the full dashboard body. + +**Required permissions** + +See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation. + + +| Action | Scope | +| ----------------- | ------------------------------------------------------------------------------------------------------- | +| `dashboards:read` |
  • `dashboards:*`
  • `dashboards:uid:*`
  • `folders:*`
  • `folders:uid:*`
| +{ .no-spacing-list } + + +**Example Get Request**: + +```http +GET /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/gdxccn HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 485 + +{ + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v1beta1", + "metadata": { + "name": "gdxccn", + "namespace": "default", + "uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX", + "resourceVersion": "2", + "generation": 2, + "creationTimestamp": "2025-03-06T19:57:18Z", + "annotations": { + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedTimestamp": "2025-03-07T02:58:36Z" + } + }, + "spec": { + "schemaVersion": 41, + "title": "New dashboard - updated", + ... + } +} +``` + +Status Codes: + +- **200** – OK +- **401** – Unauthorized +- **403** – Access denied +- **404** – Not Found + +## List Dashboards + +`GET /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards` + +Lists all dashboards in the given organization. You can control the maximum number of dashboards returned through the `limit` query parameter. You can then use the `continue` token returned to fetch the next page of dashboards. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). + +Note: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). + +**Required permissions** + +See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation. + + +| Action | Scope | +| ----------------- | ------------------------------------------------------------------------------------------------------- | +| `dashboards:read` |
  • `dashboards:*`
  • `dashboards:uid:*`
  • `folders:*`
  • `folders:uid:*`
| +{ .no-spacing-list } + + +**Example Get Request**: + +```http +GET /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards?limit=1 HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 644 + +{ + "kind": "DashboardList", + "apiVersion": "dashboard.grafana.app/v1alpha1", + "metadata": { + "resourceVersion": "1741315830000", + "continue": "org:1/start:1158/folder:" + }, + "items": [ + { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v1alpha1", + "metadata": { + "name": "gpqcmf", + "namespace": "default", + "uid": "VQyL7pNTpfGPNlPM6HRJSePrBg5dXmxr4iPQL7txLtwX", + "resourceVersion": "1", + "generation": 1, + "creationTimestamp": "2025-03-06T19:50:30Z", + "annotations": { + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedTimestamp": "2025-03-06T19:50:30Z" + } + }, + "spec": { + "schemaVersion": 41, + "title": "New dashboard", + "uid": "gpqcmf", + "version": 1, + ... + } + } + ] +} +``` + +Status Codes: + +- **200** – OK +- **401** – Unauthorized +- **403** – Access denied + +## Delete Dashboard + +`DELETE /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards/:uid` + +Deletes a dashboard via the dashboard uid. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). +- uid: the unique identifier of the dashboard to update. this will be the _name_ in the dashboard response + +**Required permissions** + +See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation. + + +| Action | Scope | +| ------------------- | ------------------------------------------------------------------------------------------------------- | +| `dashboards:delete` |
  • `dashboards:*`
  • `dashboards:uid:*`
  • `folders:*`
  • `folders:uid:*`
| +{ .no-spacing-list } + + +**Example Delete Request**: + +```http +DELETE /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/gdxccn HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 OK +Content-Type: application/json; charset=UTF-8 +Content-Length: 78 + +{ + "kind": "Status", + "apiVersion": "v1", + "metadata": {}, + "status": "Success", + "details": { + "name": "gdxccn", + "group": "dashboard.grafana.app", + "kind": "dashboards", + "uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX" + } +} +``` + +Status Codes: + +- **200** – OK +- **401** – Unauthorized +- **403** – Access denied +- **404** – Not found + +## Gets the home dashboard + +`GET /api/dashboards/home` + +Will return the home dashboard. + +**Example Request**: + +```http +GET /api/dashboards/home HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "dashboard": { + "editable":false, + "nav":[ + { + "enable":false, + "type":"timepicker" + } + ], + "style":"dark", + "tags":[], + "templating":{ + "list":[ + ] + }, + "time":{ + }, + "timezone":"browser", + "title":"Home", + "version":5 + }, + "meta": { + "isHome":true, + "canSave":false, + "canEdit":false, + "canStar":false, + "url":"", + "expires":"0001-01-01T00:00:00Z", + "created":"0001-01-01T00:00:00Z" + } +} +``` + +## Tags for Dashboard + +`GET /api/dashboards/tags` + +Get all tags of dashboards + +**Example Request**: + +```http +GET /api/dashboards/tags HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +[ + { + "term":"tag1", + "count":1 + }, + { + "term":"tag2", + "count":4 + } +] +``` + +## Dashboard Search + +See [Folder/Dashboard Search API]({{< relref "folder_dashboard_search/" >}}). + +## APIs + +### Unique identifier (uid) vs identifier (id) + +The unique identifier (uid) of a dashboard can be used to uniquely identify a dashboard within a given org. It's automatically generated if not provided when creating a dashboard. The uid allows having consistent URLs for accessing dashboards and when syncing dashboards between multiple Grafana installs, see [dashboard provisioning](/docs/grafana/latest/administration/provisioning/#dashboards) for more information. This means that changing the title of a dashboard will not break any bookmarked links to that dashboard. The uid can have a maximum length of 40 characters. -## Create / Update dashboard +The identifier (id) of a dashboard is deprecated in favor of the unique identifier (uid). + +### Create / Update dashboard `POST /api/dashboards/db` @@ -155,7 +992,7 @@ Content-Length: 97 } ``` -## Get dashboard by uid +### Get dashboard by uid `GET /api/dashboards/uid/:uid` @@ -214,7 +1051,7 @@ Status Codes: - **403** – Access denied - **404** – Not found -## Delete dashboard by uid +### Delete dashboard by uid `DELETE /api/dashboards/uid/:uid` diff --git a/docs/sources/developers/http_api/folder.md b/docs/sources/developers/http_api/folder.md index 7a65f7fa56d..6a8e2b1f771 100644 --- a/docs/sources/developers/http_api/folder.md +++ b/docs/sources/developers/http_api/folder.md @@ -16,19 +16,388 @@ labels: title: Folder HTTP API --- -# Folder API +# New Folders APIs > If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information. +> To view more about the new api structure, refer to [API overview]({{< ref "apis" >}}). + +### Get all folders + +`GET /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders` + +Returns all folders that the authenticated user has permission to view within the given organization. Use the `limit` query parameter to control the maximum number of dashboards returned. To retrieve additional dashboards, utilize the `continue` token provided in the response to fetch the next page. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). + +**Required permissions** + +See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation. + +| Action | Scope | +| -------------- | ----------- | +| `folders:read` | `folders:*` | + +**Example Request**: + +```http +GET /apis/folder.grafana.app/v1beta1/namespaces/default/folders?limit=1 HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json +{ + "kind": "FolderList", + "apiVersion": "folder.grafana.app/v1beta1", + "metadata": { + "continue": "org:1/start:1158/folder:" + }, + "items": [ + { + "kind": "Folder", + "apiVersion": "folder.grafana.app/v1beta1", + "metadata": { + "name": "aef30vrzxs3y8d", + "namespace": "default", + "uid": "KCtv1FXDsJmTYQoTgcPnfuwZhDZge3uMpXOefaOHjb4X", + "resourceVersion": "1741343686000", + "creationTimestamp": "2025-03-07T10:34:46Z", + "annotations": { + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedTimestamp": "2025-03-07T10:34:46Z" + } + }, + "spec": { + "title": "example" + } + } + ] +} +``` + +Status Codes: + +- **200** – OK +- **401** – Unauthorized +- **403** – Access Denied + +### Get folder by uid + +`GET /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders/:uid` + +Will return the folder given the folder uid. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). +- uid: the unique identifier of the folder to update. this will be the _name_ in the folder response + +**Required permissions** + +See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation. + +| Action | Scope | +| -------------- | ----------- | +| `folders:read` | `folders:*` | + +**Example Request**: + +```http +GET /apis/folder.grafana.app/v1beta1/namespaces/default/folders/aef30vrzxs3y8d HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json +{ + "kind": "Folder", + "apiVersion": "folder.grafana.app/v1beta1", + "metadata": { + "name": "aef30vrzxs3y8d", + "namespace": "default", + "uid": "KCtv1FXDsJmTYQoTgcPnfuwZhDZge3uMpXOefaOHjb4X", + "resourceVersion": "1741343686000", + "creationTimestamp": "2025-03-07T10:34:46Z", + "annotations": { + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedTimestamp": "2025-03-07T10:34:46Z", + "grafana.app/folder": "fef30w4jaxla8b" + } + }, + "spec": { + "title": "test" + } +} +``` + +Note the annotation `grafana.app/folder` which contains the uid of the parent folder. + +Status Codes: + +- **200** – Found +- **401** – Unauthorized +- **403** – Access Denied +- **404** – Folder not found + +### Create folder + +`POST /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders` + +Creates a new folder. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). + +**Required permissions** + +See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation. + +`folders:create` allows creating folders and subfolders. If granted with scope `folders:uid:general`, allows creating root level folders. Otherwise, allows creating subfolders under the specified folders. + +| Action | Scope | +| ---------------- | ----------- | +| `folders:create` | `folders:*` | +| `folders:write` | `folders:*` | + +**Example Request**: + +```http +POST /apis/folder.grafana.app/v1beta1/namespaces/default/folders HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +{ + "metadata": { + "name": "aef30vrzxs3y8d", + "annotations": { + "grafana.app/folder": "fef30w4jaxla8b" + } + } + "spec": { + "title": "child-folder" + }, +} +``` + +JSON Body schema: + +- **metadata.name** – The Grafana [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}). If you do not want to provide this, set metadata.generateName to the prefix you would like for the uid. +- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the parent folder under which the folder should be created. Requires nested folders to be enabled. +- **spec.title** – The title of the folder. + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json +{ + "kind": "Folder", + "apiVersion": "folder.grafana.app/v1beta1", + "metadata": { + "name": "eef33r1fprd34d", + "namespace": "default", + "uid": "X8momvVZnsXdOqvLD9I4ngqLVif2CgRWXHy9xb2UgjQX", + "resourceVersion": "1741320415009", + "creationTimestamp": "2025-03-07T04:06:55Z", + "labels": { + "grafana.app/deprecatedInternalID": "1159" + }, + "annotations": { + "grafana.app/folder": "fef30w4jaxla8b", + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb" + } + }, + "spec": { + "title": "child-folder" + } +} +``` + +Status Codes: + +- **201** – Created +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Access denied +- **409** – Conflict (folder with the same uid already exists) + +### Update folder + +`PUT /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders/:uid` + +Updates an existing folder identified by uid. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). +- uid: the unique identifier of the folder to update. this will be the _name_ in the folder response + +**Required permissions** + +See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation. + +| Action | Scope | +| --------------- | ----------- | +| `folders:write` | `folders:*` | + +**Example Request**: + +```http +PUT /apis/folder.grafana.app/v1beta1/namespaces/default/folders/fef30w4jaxla8b HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +"metadata": { + "name": "aef30vrzxs3y8d", + "annotations": { + "grafana.app/folder": "xkj92m5pqw3vn4" + } + } + "spec": { + "title": "updated title" + }, +``` + +JSON Body schema: + +- **metadata.name** – The [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}) of the folder. +- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the parent folder under which the folder should be - update this to move the folder under a different parent folder. Requires nested folders to be enabled. +- **spec.title** – The title of the folder. + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "kind": "Folder", + "apiVersion": "folder.grafana.app/v1beta1", + "metadata": { + "name": "fef30w4jaxla8b", + "namespace": "default", + "uid": "YaWLsFrMwEaTlIQwX2iMnhHlJuZHtZugps50BQoyjXEX", + "resourceVersion": "1741345736000", + "creationTimestamp": "2025-03-07T11:08:56Z", + "annotations": { + "grafana.app/folder": "xkj92m5pqw3vn4", + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedTimestamp": "2025-03-07T11:08:56Z" + } + }, + "spec": { + "title": "updated title" + } +} +``` + +Status Codes: + +- **200** – Updated +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **403** – Access Denied +- **404** – Folder not found +- **412** – Precondition failed (the folder has been changed by someone else). With this status code, the response body will have the following properties: + +```http +HTTP/1.1 412 Precondition Failed +Content-Type: application/json; charset=UTF-8 +Content-Length: 97 + +{ + "message": "The folder has been changed by someone else", + "status": "version-mismatch" +} +``` + +### Delete folder + +`DELETE /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders/:uid` + +Deletes an existing folder identified by UID along with all dashboards (and their alerts) stored in the folder. This operation cannot be reverted. + +If [Grafana Alerting]({{< relref "/docs/grafana/latest/alerting" >}}) is enabled, you can set an optional query parameter `forceDeleteRules=false` so that requests will fail with 400 (Bad Request) error if the folder contains any Grafana alerts. However, if this parameter is set to `true` then it will delete any Grafana alerts under this folder. + +- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}). +- uid: the unique identifier of the folder to delete. this will be the _name_ in the folder response + +**Required permissions** + +See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation. + +| Action | Scope | +| ---------------- | ----------- | +| `folders:delete` | `folders:*` | + +**Example Request**: + +```http +DELETE /apis/folder.grafana.app/v1beta1/namespaces/default/folders/fef30w4jaxla8b HTTP/1.1 +Accept: application/json +Content-Type: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +``` + +**Example Response**: + +```http +HTTP/1.1 200 +Content-Type: application/json + +{ + "kind": "Folder", + "apiVersion": "folder.grafana.app/v1beta1", + "metadata": { + "name": "fef30w4jaxla8b", + "namespace": "default", + "uid": "YaWLsFrMwEaTlIQwX2iMnhHlJuZHtZugps50BQoyjXEX", + "resourceVersion": "1741345736000", + "creationTimestamp": "2025-03-07T11:08:56Z", + "annotations": { + "grafana.app/folder": "xkj92m5pqw3vn4", + "grafana.app/createdBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedBy": "service-account:cef2t2rfm73lsb", + "grafana.app/updatedTimestamp": "2025-03-07T11:08:56Z" + } + }, + "spec": { + "title": "updated title" + } +} +``` + +Status Codes: + +- **200** – Deleted +- **401** – Unauthorized +- **400** – Bad Request +- **403** – Access Denied +- **404** – Folder not found + +## APIs + ## Identifier (id) vs unique identifier (uid) -The identifier (id) of a folder is an auto-incrementing numeric value and is only unique per Grafana install. - -The unique identifier (uid) of a folder can be used for uniquely identify folders between multiple Grafana installs. It's automatically generated if not provided when creating a folder. The uid allows having consistent URLs for accessing folders and when syncing folders between multiple Grafana installs. This means that changing the title of a folder will not break any bookmarked links to that folder. +The unique identifier (uid) of a folder can be used for uniquely identify folders within an org. It's automatically generated if not provided when creating a folder. The uid allows having consistent URLs for accessing folders and when syncing folders between multiple Grafana installs. This means that changing the title of a folder will not break any bookmarked links to that folder. The uid can have a maximum length of 40 characters. -## Get all folders +The identifier (id) of a folder is deprecated in favor of the unique identifier (uid). + +### Get all folders `GET /api/folders` @@ -75,7 +444,7 @@ Content-Type: application/json ] ``` -## Get folder by uid +### Get folder by uid `GET /api/folders/:uid` @@ -133,7 +502,7 @@ Status Codes: - **403** – Access Denied - **404** – Folder not found -## Create folder +### Create folder `POST /api/folders` @@ -207,7 +576,7 @@ Status Codes: - **403** – Access Denied - **412** - Folder already exists -## Update folder +### Update folder `PUT /api/folders/:uid` @@ -296,7 +665,7 @@ Content-Length: 97 } ``` -## Delete folder +### Delete folder `DELETE /api/folders/:uid` @@ -342,7 +711,7 @@ Status Codes: - **403** – Access Denied - **404** – Folder not found -## Move folder +### Move folder `POST /api/folders/:uid/move` diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index ad403d30d28..ff082322cb8 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -855,6 +855,23 @@ Path to the default home dashboard. If this value is empty, then Grafana uses St On Linux, Grafana uses `/usr/share/grafana/public/dashboards/home.json` as the default home dashboard location. {{< /admonition >}} +### `[dashboard_cleanup]` + +Settings related to cleaning up associated dashboards information if the dashboard was deleted through /apis. + +#### `interval` + +How often to run the job to cleanup associated resources. The default interval is `30s`. The minimum allowed value is `10s` to ensure the system isn't overloaded. + +The interval string must include a unit suffix (ms, s, m, h), e.g. 30s or 1m. + +#### `batch_size` + +Number of deleted dashboards to process in each batch during the cleanup process. +Default: `10`, Minimum: `5`, Maximum: `200`. + +Increasing this value allows processing more dashboards in each cleanup cycle but may impact system performance. +
### `[datasources]`