Grafana
by @byungkyu
Grafana API integration with managed authentication. This is a write-capable integration — it can read, create, update, and delete dashboards, data sources,...
clawhub install grafana-api📖 About This Skill
name: grafana description: | Grafana API integration with managed authentication. This is a write-capable integration — it can read, create, update, and delete dashboards, data sources, folders, annotations, alerts, and teams in your Grafana instance. Use this skill when users want to interact with Grafana for monitoring, visualization, and observability. All write operations (creating/updating/deleting dashboards, folders, data sources, alerts, or teams) require explicit user approval with specific resource identifiers before execution. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). compatibility: Requires network access and valid Maton API key metadata: author: maton version: "1.0" clawdbot: emoji: homepage: "https://maton.ai" requires: env: - MATON_API_KEY
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
1. Sign in or create an account at maton.ai 2. Go to maton.ai/settings 3. 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
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-db for dashboards, dash-folder for 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 pageResponse:
{
"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 requestsresponse = 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
/api/search?type=dash-db to find dashboard UIDs/api/v1/provisioning/...)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 |
Resources
💡 Examples
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
📋 Tips & Best Practices
/api/search?type=dash-db to find dashboard UIDs/api/v1/provisioning/...)