Loading...
Loading...
Invoke the `groundcover` Go CLI to manage Groundcover resources (dashboards, monitors, silences, connected apps, notification routes, API keys, policies, integrations, pipelines, workflows) AND to answer production observability questions by querying logs, traces, metrics, k8s inventory, and k8s events. Use whenever a task needs an authenticated call against the Groundcover API or whenever the user is debugging a prod issue and asks things like "why is X erroring in prod", "show me logs for service Y", "what's the p99 latency on Z", "what pods are crashlooping", "search traces for slow requests", "any k8s events for namespace N", "is service S receiving traffic", "list groundcover monitors", "create a silence", "update notification route", "hit a groundcover endpoint". Covers required env vars, the SDK-backed vs raw command split, and concrete request-body templates for logs/traces/metrics/k8s so the CLI can be driven from anywhere.
npx skill4agent add paymog/groundcover-cli groundcover-cligroundcoverbrew install paymog/tap/groundcoverpaymog/groundcover-cliexport GROUNDCOVER_API_KEY=gcsa_... # must be a service-account keygcsa_Invalid token prefix. Expected 'gcsa_'Invalid token length (N, expected 40)gcsa_…gcik_…gcsa_gcsa_groundcover service-accounts createexport GROUNDCOVER_API_KEY=gcsa_…--base-url https://api.groundcover.com--backend-id groundcover--tenant-uuidGROUNDCOVER_TENANT_UUIDX-Tenant-UUIDraw …raw grafana …raw grafana …gcsa_glsa_…GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN--grafana-tokenGROUNDCOVER_BACKEND_IDGROUNDCOVER_TENANT_UUIDGROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKENGROUNDCOVER_BASE_URLGC_*--api-key--backend-id--tenant-uuid--grafana-token--base-urlGROUNDCOVER_API_KEY~/.config/groundcover/profiles.yamlgroundcover auth login [name] --backend-id <id> # prompts for key (or --key); validates before saving
groundcover auth list # * marks default
groundcover auth default <name> # change default
groundcover --profile <name> <command> # use a profile for one command
groundcover auth status # show resolved source
groundcover auth token # print resolved key
groundcover auth logout <name> [-f]--api-keyGROUNDCOVER_API_KEY--profile <name>--profileGROUNDCOVER_PROFILE--timeout--raw| Surface | When to use |
|---|---|
SDK-backed ( | First choice. Stable contracts, typed request bodies. |
Raw HAR-derived ( | Endpoint missing from SDK, or you need a runner feature SDK lacks (see below). |
--set dotted.path=value--query key=value--body-file--body-jsonX-Tenant-UUID--tenant-uuidGROUNDCOVER_TENANT_UUIDraw grafana …glsa_groundcover raw list--body-file--body-json--rawgroundcover raw …filtersvelocitysearchattributesdetailserrorsfiltersinsightslatenciesrequestsvalues-distributionsearchcardinalitycardinality-graphdiscoverylabels-cardinalityquery-rangeresources errors|latencies|list|requestsquerynameskeysvaluesprometheus api query--query query='up'grafana searchgrafana dashboards get|save|deletegrafana dashboards permissions get|updategrafana dashboards versions|get|restoregrafana folders list|get|create|update|deletegrafana folders permissions get|updategrafana annotations list|create|update|deletegrafana prometheus rulesgrafana datasources label-valuesgrafana ds queryinstances filters|query|timelinelabels keyssilencessummary filters|querytimelineconfigmaps|cronjob|daemonsets|deployments|jobs|pods|pvcs|replicasets|statefulsets listcontainer infocontext eventsnamespaces info|listnodes info-with-resources|list|resources|usage top10pod container usagepods status-over-timeworkloads availability|events|usage top10network connections|cross-az|cross-az-regions|partners|throughput|top-connectionsevents search-time-seriesclustersworkloadsevents-searchevents-over-timeinfra hosts info-with-resourcesresources apis errors|filters|latencies|list|requestsrum sessions filters|querysources listpipelines logs current-statspipelines traces current-statsrbac seatsUsagerbac tenant ai-settingsrbac tenant settingsbackend settingsbilling methodagent token-budgets|token-usage|token-usage history|token-usage tenantstorage-management get|update --data-type <logs|traces|events|measurements|monitor_instance>graphgraph filtersviews memberviews member defaultsmigrationsconnectors list org|personalsynthetics rulesaggregations metrics config|config defaultintegrations data configgroundcover raw listgroundcover raw list <group>groundcover <resource> list [--query '<filter>']
groundcover <resource> get <id>
groundcover <resource> create --body-file <file> # or --body-json '<json>'
groundcover <resource> update <id> --body-file <file>
groundcover <resource> delete <id>.json.yamlarchive <id>restore <id>--query 'monitor_name = "cpu"'list --active--query 'type:slack-webhook'--query 'prod'hash <id>groundcover api-keys list
groundcover api-keys create --body-file key.json
groundcover service-accounts list
groundcover service-accounts create --body-file sa.json
groundcover ingestion-keys list
groundcover policies list
groundcover policies apply --body-file policy.json
groundcover policies audit-trail <id>getcreateupdatedeletegroundcover logs-pipeline get
groundcover metrics-pipeline get
groundcover traces-pipeline get
groundcover metrics-aggregator getgroundcover integrations list
groundcover integrations describe <type>
groundcover integrations create <type> --body-file config.json
groundcover integrations update <type> <id> --body-file config.jsongroundcover logs search --body-file body.json
groundcover traces search --body-file body.json
groundcover metrics query --body-file body.json # PromQL range/instant
groundcover metrics names --body-file body.json # list metric names
groundcover metrics keys --body-file body.json # label keys for a metric
groundcover metrics values --body-file body.json # label values for a key
groundcover search discovery --body-file body.json # facet / discovery
groundcover search keys --body-file body.json
groundcover search values --body-file body.json
groundcover k8s clusters --body-file body.json
groundcover k8s workloads --body-file body.json
groundcover k8s events-search --body-file body.json
groundcover k8s events-over-time --body-file body.jsonjq--raw2026-05-28T00:00:00Zdate -ulogs searchtraces searchk8s events-search{
"start": "2026-05-28T00:00:00Z",
"end": "2026-05-28T01:00:00Z",
"query": "<GCQL>",
"filters": "",
"sources": []
}metrics query{
"Promql": "rate(http_requests_total[5m])",
"Start": "2026-05-28T00:00:00Z",
"End": "2026-05-28T01:00:00Z",
"Step": "60",
"QueryType": "range",
"Conditions": []
}"QueryType": "instant"Stepk8s workloadsk8s clusters{
"conditions": [],
"sources": [],
"gcqlFilter": "namespace = 'prod'",
"limit": 100
}groundcover raw list # list groups
groundcover raw list <group> # list commands in group
groundcover raw k8s clusters list
groundcover raw dashboards get --dashboard-id <id>
groundcover raw metrics query-range --body-file body.json
groundcover raw prometheus api query --query query='up'
groundcover raw grafana dashboards get --dashboard-uid <uid>
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana ds query --body-file query.jsonlogstraceseventsmeasurementsmonitor_instancegroundcover raw storage-management get --data-type logs --raw \
| jq '{retention,version,cold_move_duration,cold_volume,custom_rules:(.custom_rules // [])}' \
> storage.json
# Edit storage.json, preserving every writable field and the complete rule list.
groundcover raw storage-management update --data-type logs --body-file storage.jsongetversionretentioncold_move_durationcold_volumecustom_rulescustom_rulesversionnameretentionfilters/grafanadashboardsraw grafana …gcsa_gcsa_/grafana/api/*index.htmltext/htmlglsa_…GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN--grafana-tokenraw grafana …github.com/groundcover-com/cligroundcover~/.groundcover/bin/groundcovergroundcoversh -c "$(curl -fsSL https://groundcover.com/install.sh)" # installs to ~/.groundcover/bin
~/.groundcover/bin/groundcover auth login # auth flow
~/.groundcover/bin/groundcover auth generate-service-account-token # prints glsa_… once
export GROUNDCOVER_GRAFANA_SERVICE_ACCOUNT_TOKEN=glsa_... # hand it to this CLIgroundcover~/.groundcover/bingroundcover raw grafana search --query query='service slo' --query folderUIDs=general
groundcover raw grafana dashboards get --dashboard-uid streamling-pipeline-slo
groundcover raw grafana dashboards save --body-file dashboard.json
groundcover raw grafana folders list
groundcover raw grafana datasources label-values --datasource-uid <uid> --label project_id --query start=<unix> --query end=<unix>
groundcover raw grafana ds query --query ds_type=prometheus --body-file query.jsonhttps://app.groundcover.comhttps://api.groundcover.com--base-url--body-json--body-file--set dotted.path=value--query key=value--dashboard-id--raw--body-*--setEND=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ) # GNU: date -u -d '1 hour ago' +%FT%TZ
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND level = \"error\"",
filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin | jq '.[] | {ts:.timestamp, pod:.pod_name, body:(.body|fromjson?)}'returns a bare JSON array at the root —logs search, notjq '.[]'. The human-readable message lives in the.logs[]field as a JSON string (parse withbody). Useful top-level fields:fromjson,pod_name,container_name,namespace,workload(normalized lowercase —level/error/warning, not thedebug/WARNyou see insideDEBG),body,cluster,timestamp.trace_id
END=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
Promql: "histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"api\"}[5m])))",
Start: $s, End: $e, Step: "60", QueryType: "range", Conditions: []
}' | groundcover metrics query --body-file /dev/stdinjq -n '{ conditions: [], sources: [], gcqlFilter: "status != \"Running\"", limit: 200 }' \
| groundcover k8s workloads --body-file /dev/stdin \
| jq '.workloads[] | select(.restarts > 0) | {name, namespace, status, restarts}'END=$(date -u +%FT%TZ); START=$(date -u -v-1H +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "namespace = \"prod\" AND type = \"Warning\"",
filters: "", sources: []
}' | groundcover k8s events-search --body-file /dev/stdinEND=$(date -u +%FT%TZ); START=$(date -u -v-30M +%FT%TZ)
jq -n --arg s "$START" --arg e "$END" '{
start:$s, end:$e,
query: "service_name = \"api\" AND duration_ms > 1000",
filters: "", sources: []
}' | groundcover traces search --body-file /dev/stdin \
| jq -r '.[] | .trace_id' | head -5 \
| while read tid; do
jq -n --arg s "$START" --arg e "$END" --arg t "$tid" '{
start:$s, end:$e, query: ("trace_id = \"" + $t + "\""), filters: "", sources: []
}' | groundcover logs search --body-file /dev/stdin
done# What metric names match a prefix?
jq -n '{ prefix: "http_" }' | groundcover metrics names --body-file /dev/stdin
# What labels does a specific metric have?
jq -n '{ metric: "http_request_duration_seconds" }' | groundcover metrics keys --body-file /dev/stdin
# What values does a label take?
jq -n '{ metric: "http_request_duration_seconds", key: "service" }' \
| groundcover metrics values --body-file /dev/stdinservice_name = "api"\"duration_ms > 1000status_code >= 500ANDORNOTmessage ~ "timeout"message ~* "^panic:"level IN ("error", "fatal")container = "my-service" AND "database unavailable"IN(...)freetext 'in(...)' is not supportedlevel = "error" OR level = "fatal"queryquery: ""pipeline cannot be niltrace_id IS NOT NULLsearch discoverysearch keysLimitTypekeysbodygroundcover monitors list --query 'monitor_name ~ "ingest"'
groundcover silences create --body-json '{"monitor_id":"<id>","duration_seconds":3600,"reason":"deploy"}'uuidid.idarchivedTimestamp0001-01-01T00:00:00.000Zgroundcover dashboards list \
| jq -r '.[] | select(.archivedTimestamp == "0001-01-01T00:00:00.000Z") | .uuid' \
| while read id; do groundcover dashboards get "$id" > "dashboards/$id.json"; donegroundcover dashboards get <uuid>.presetfromjsongroundcover dashboards list | jq -r '.[] | select(.name|test("prod";"i")) | "\(.uuid)\t\(.name)"'
groundcover dashboards get <uuid> | jq -r '.preset | fromjson | .variables'
# each var: { kind:"list", spec:{ values:{default:["*"]}, datasource:{key,kind,metric}, variableName } }variables$<variableName>namekeyvaluesmyapp*datasource"metrics"refiner.metricdatasource.metric*deploymentshard: primary["*"]app_kubernetes_io_instancemyappmyapp-web-0myapp*VARS=$(jq -cn '{
"$cluster":{name:"cluster",key:"cluster",values:["example-cluster"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$deployment":{name:"deployment",key:"deployment",values:["*"],datasource:"metrics",refiner:{metric:"my_metric_total"}},
"$app_kubernetes_io_instance":{name:"app_kubernetes_io_instance",key:"app_kubernetes_io_instance",values:["myapp*"],datasource:"metrics",refiner:{metric:"my_metric_total"}}
}')
ENC=$(jq -rn --arg v "$VARS" '$v|@uri')
echo "https://app.groundcover.com/dashboards?backendId=groundcover&tenantUUID=<your-tenant-uuid>&duration=Last+1+hour&viewId=<uuid>&qb-disable-auto-focus=true&variables=${ENC}"viewId=<uuid>/dashboards/<uuid>durationLast+1+hourLast+5+minutesgroundcover raw prometheus api query --query query='up{namespace="prod"}'dashboards get/list/create/update/archive/restoredashboards get <uuid>.presetfromjsonjson.loads{
"spec": { "layoutType": "ordered", "crosshairSyncEnabled": true },
"layout": [ /* one entry per TOP-LEVEL item, placed on a 24-col grid */ ],
"widgets": [ /* the actual panels, referenced by id from layout */ ],
"duration": "Last 6 hours", // UI duration string
"variables": [ /* template vars, same shape as the deep-link recipe */ ],
"schemaVersion": 7
}layout{ "id":"A", "h":12, "w":24, "x":0, "y":0, "minH":8 }w:8w:12ididywidgets{ id, name, type, queries:[{id, expr, dataType:"metrics", editorMode:"editor"}], visualizationConfig:{ type, selectedUnit, ... } }expr$vartime-seriesstattextcontentsection{ id, name, type:"section", color, isCollapsed:false }children{ id, h, w:24, x:0, y:0, children:[ {h,w,x,y,id,minH}, ... ] }y:2dashboards update <uuid>currentRevision.revisionNumberField validation for 'CurrentRevision' failed on the 'required_if' taggetgroundcover dashboards get <uuid> > d.json
# NEW_PRESET must be the preset object re-serialized to a STRING
jq -n --slurpfile d d.json --arg preset "$NEW_PRESET" '{
name: $d[0].name, viewType: ($d[0].viewType // "explore"),
status: "active", currentRevision: $d[0].revisionNumber, preset: $preset
}' | groundcover dashboards update <uuid> --body-file /dev/stdinDashboard validation failed400 {"message":"Dashboard validation failed"}visualizationConfig.type:"stat"layoutchildren[]textcolorgraypurpletealgreenblueredyelloworangelast_over_timelast_over_time(<selector>[30m])max(last_over_time(my_metric{...}[30m])) # NOT max(my_metric{...})*_over_time100 * avg_over_time((...)[7d:5m])expr$var=~".+"metrics queryx+w ≤ 24childrenstatstat=average/maximum/minimumstat="average"envinstance_idinstanceidSTARTENDdate -ugroundcover logs searchservice_namelevel = "error"groundcover traces searchduration_msstatus_codeservice_nametrace_idtrace_id = "..."groundcover metrics queryrangeinstantgroundcover k8s workloadsgroundcover k8s events-searchWarninggroundcover search discoverymetrics namesmetrics keysmetrics valuesgroundcover monitors list --query …groundcover silences creategroundcover --helpgroundcover <resource> --helpgroundcover raw listgroundcover raw list <group>401 UnauthorizedInvalid token prefixInvalid token lengthgcsa_gcik_401gcsa_GROUNDCOVER_TENANT_UUIDGROUNDCOVER_BASE_URLget <id>logs searchtraces searchevents-searchstartendsearch discoverysearch keysservice_name = "api""service_name = \"api\""metrics queryPromqlStartEndStepQueryTypeStep"60""QueryType": "instant"Stepmetrics query400 metricsQueryBadRequestStartEndTimeQueryTypeQueryType:"range"Stepraw prometheus api querymetrics query.data.result // .resultvalues[unix_ts, "stringvalue"][.values[][1]] | map(tonumber) | addstrftimelogs searchcontext deadline exceededcontainerpod--timeout--timeout 120sORmetrics querymonitorsmonitors getupdate--rawmonitors get <uuid> > mon.yamlmonitors update <uuid> --body-file mon.yaml{"status":202}getmodel.queries[].sqlPipeline.filters.conditions[]{key, origin: root, type, filters:[{op, value}]}type: freetextop: phrase_searchinstantRollup1 minutethresholds[]evaluationInterval.{interval, pendingFor}monitors list --query 'monitor_name ~ "..."'regexp requires a string column typemonitor_name = "exact"jqgroundcover-cligo run ./scripts/generate-commands.go <har>