Grafana
Access Grafana dashboards, data sources, folders, annotations, and alerts via managed API authentication.
Quick Start
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/grafana/api/search?type=dash-db')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://api.maton.ai/grafana/{native-api-path}
Maton proxies requests to your Grafana instance and automatically injects authentication.
Authentication
All requests require the Maton API key:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Grafana connections at https://api.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=grafana&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'grafana'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Open the returned url in a browser to complete authentication. You'll need to provide your Grafana service account token. Use a dedicated, least-privilege service account token for this integration — limit it to the specific organization, folders, and resources needed for the task. Avoid admin-role tokens unless specifically required. Rotate the token periodically and revoke it immediately when the connection is no longer needed.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Security & Permissions
- Access is scoped to dashboards, data sources, folders, annotations, alerts, and teams within the connected Grafana instance. The integration inherits the permissions of the service account token used during connection setup — use least-privilege tokens scoped to the needed organization and folders. Prefer a non-production instance for exploratory work. Remove the connection when no longer needed.
- Default to read-only operations. Always start by listing or retrieving resources to confirm identifiers before proposing any changes.
- All write operations require explicit user approval with specific identifiers. Before executing any POST, PUT, PATCH, or DELETE call:
- Retrieve and display the target resource (dashboard title/UID, folder name, data source name, alert rule) so the user can verify.
- Clearly describe the intended effect (e.g., "This will delete dashboard 'Production Overview' (UID: abc-123) from the 'Ops' folder").
- Wait for explicit user confirmation before proceeding.
- High-impact operations require extra caution. Deleting dashboards, modifying alert rules, changing data source configurations, and reorganizing folders can affect monitoring and observability workflows. These actions must include a summary of consequences and require confirmation.
API Reference
Organization & User
Get Current Organization
GET /grafana/api/org
Response:
{
"id": 1,
"name": "Main Org.",
"address": {
"address1": "",
"address2": "",
"city": "",
"zipCode": "",
"state": "",
"country": ""
}
}
Get Current User
GET /grafana/api/user
Response:
{
"id": 1,
"uid": "abc123",
"email": "user@example.com",
"name": "User Name",
"login": "user",
"orgId": 1,
"isGrafanaAdmin": false
}
Dashboards
Search Dashboards
GET /grafana/api/search?type=dash-db
Query Parameters:
type-dash-dbfor dashboards,dash-folderfor foldersquery- Search query stringtag- Filter by tagfolderIds- Filter by folder IDslimit- Max results (default 1000)
Response:
[
{
"id": 1,
"uid": "abc123",
"title": "My Dashboard",
"uri": "db/my-dashboard",
"url": "/d/abc123/my-dashboard",
"type": "dash-db",
"tags": ["production"],
"isStarred": false
}
]
Get Dashboard by UID
GET /grafana/api/dashboards/uid/{uid}
Response:
{
"meta": {
"type": "db",
"canSave": true,
"canEdit": true,
"canAdmin": true,
"canStar": true,
"slug": "my-dashboard",
"url": "/d/abc123/my-dashboard",
"expires": "0001-01-01T00:00:00Z",
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-02T00:00:00Z",
"version": 1
},
"dashboard": {
"id": 1,
"uid": "abc123",
"title": "My Dashboard",
"tags": ["production"],
"panels": [...],
"schemaVersion": 30,
"version": 1
}
}
Create/Update Dashboard
POST /grafana/api/dashboards/db
Content-Type: application/json
{
"dashboard": {
"title": "New Dashboard",
"panels": [],
"schemaVersion": 30,
"version": 0
},
"folderUid": "optional-folder-uid",
"overwrite": false
}
Response:
{
"id": 1,
"uid": "abc123",
"url": "/d/abc123/new-dashboard",
"status": "success",
"version": 1,
"slug": "new-dashboard"
}
Delete Dashboard
DELETE /grafana/api/dashboards/uid/{uid}
Response:
{
"title": "My Dashboard",
"message": "Dashboard My Dashboard deleted",
"id": 1
}
Get Home Dashboard
GET /grafana/api/dashboards/home
Folders
List Folders
GET /grafana/api/folders
Response:
[
{
"id": 1,
"uid": "folder123",
"title": "My Folder",
"url": "/dashboards/f/folder123/my-folder",
"hasAcl": false,
"canSave": true,
"canEdit": true,
"canAdmin": true
}
]
Get Folder by UID
GET /grafana/api/folders/{uid}
Create Folder
POST /grafana/api/folders
Content-Type: application/json
{
"title": "New Folder"
}
Response:
{
"id": 1,
"uid": "folder123",
"title": "New Folder",
"url": "/dashboards/f/folder123/new-folder",
"hasAcl": false,
"canSave": true,
"canEdit": true,
"canAdmin": true,
"version": 1
}
Update Folder
PUT /grafana/api/folders/{uid}
Content-Type: application/json
{
"title": "Updated Folder Name",
"version": 1
}
Delete Folder
DELETE /grafana/api/folders/{uid}
Data Sources
List Data Sources
GET /grafana/api/datasources
Response:
[
{
"id": 1,
"uid": "ds123",
"orgId": 1,
"name": "Prometheus",
"type": "prometheus",
"access": "proxy",
"url": "http://prometheus:9090",
"isDefault": true,
"readOnly": false
}
]
Get Data Source by ID
GET /grafana/api/datasources/{id}
Get Data Source by UID
GET /grafana/api/datasources/uid/{uid}
Get Data Source by Name
GET /grafana/api/datasources/name/{name}
Create Data Source
POST /grafana/api/datasources
Content-Type: application/json
{
"name": "New Prometheus",
"type": "prometheus",
"url": "http://prometheus:9090",
"access": "proxy",
"isDefault": false
}
Update Data Source
PUT /grafana/api/datasources/{id}
Content-Type: application/json
{
"name": "Updated Prometheus",
"type": "prometheus",
"url": "http://prometheus:9090",
"access": "proxy"
}
Delete Data Source
DELETE /grafana/api/datasources/{id}
Annotations
List Annotations
GET /grafana/api/annotations
Query Parameters:
from- Epoch timestamp (ms)to- Epoch timestamp (ms)dashboardId- Filter by dashboard IDdashboardUID- Filter by dashboard UIDpanelId- Filter by panel IDtags- Filter by tags (comma-separated)limit- Max results
Create Annotation
POST /grafana/api/annotations
Content-Type: application/json
{
"dashboardUID": "abc123",
"time": 1609459200000,
"text": "Deployment completed",
"tags": ["deployment", "production"]
}
Response:
{
"message": "Annotation added",
"id": 1
}
Update Annotation
PUT /grafana/api/annotations/{id}
Content-Type: application/json
{
"text": "Updated annotation text",
"tags": ["updated"]
}
Delete Annotation
DELETE /grafana/api/annotations/{id}
Teams
Search Teams
GET /grafana/api/teams/search
Query Parameters:
query- Search querypage- Page numberperpage- Results per page
Response:
{
"totalCount": 1,
"teams": [
{
"id": 1,
"orgId": 1,
"name": "Engineering",
"email": "engineering@example.com",
"memberCount": 5
}
],
"page": 1,
"perPage": 1000
}
Get Team by ID
GET /grafana/api/teams/{id}
Create Team
POST /grafana/api/teams
Content-Type: application/json
{
"name": "New Team",
"email": "team@example.com"
}
Update Team
PUT /grafana/api/teams/{id}
Content-Type: application/json
{
"name": "Updated Team Name"
}
Delete Team
DELETE /grafana/api/teams/{id}
Alert Rules (Provisioning API)
List Alert Rules
GET /grafana/api/v1/provisioning/alert-rules
Get Alert Rule
GET /grafana/api/v1/provisioning/alert-rules/{uid}
List Alert Rules by Folder
GET /grafana/api/ruler/grafana/api/v1/rules
Service Accounts
Search Service Accounts
GET /grafana/api/serviceaccounts/search
Response:
{
"totalCount": 1,
"serviceAccounts": [
{
"id": 1,
"name": "api-service",
"login": "sa-api-service",
"orgId": 1,
"isDisabled": false,
"role": "Editor"
}
],
"page": 1,
"perPage": 1000
}
Plugins
List Plugins
GET /grafana/api/plugins
Response:
[
{
"name": "Prometheus",
"type": "datasource",
"id": "prometheus",
"enabled": true,
"pinned": false
}
]
Code Examples
JavaScript
const response = await fetch('https://api.maton.ai/grafana/api/search?type=dash-db', {
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
});
const dashboards = await response.json();
console.log(dashboards);
Python
import os
import requests
response = requests.get(
'https://api.maton.ai/grafana/api/search?type=dash-db',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'
}
)
print(response.json())
Notes
- Dashboard UIDs are unique identifiers used in most operations
- Use
/api/search?type=dash-dbto find dashboard UIDs - Folder operations require folder UIDs
- Some admin operations (list all users, orgs) require elevated permissions
- Alert rules use the provisioning API (
/api/v1/provisioning/...) - Annotations require epoch timestamps in milliseconds
Error Handling
| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Invalid request |
| 401 | Invalid or missing authentication |
| 403 | Permission denied |
| 404 | Resource not found |
| 409 | Conflict (e.g., duplicate name) |
| 412 | Precondition failed (version mismatch) |
| 422 | Unprocessable entity |