curl --request GET \
--url {scheme}://{host}:{port}/{basePath}/analytics/mttd \
--header 'X-API-Key: <api-key>'import requests
url = "{scheme}://{host}:{port}/{basePath}/analytics/mttd"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('{scheme}://{host}:{port}/{basePath}/analytics/mttd', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "62437",
CURLOPT_URL => "{scheme}://{host}:{port}/{basePath}/analytics/mttd",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "{scheme}://{host}:{port}/{basePath}/analytics/mttd"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("{scheme}://{host}:{port}/{basePath}/analytics/mttd")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("{scheme}://{host}:{port}/{basePath}/analytics/mttd")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"apiVersion": "v1",
"kind": "MTTDMetrics",
"metadata": {
"totalCount": 3,
"page": 1,
"pageSize": 3
},
"items": [
{
"date": "2026-03-17",
"value": 45,
"count": 3
},
{
"date": "2026-03-18",
"value": 28,
"count": 8
},
{
"date": "2026-03-19",
"value": 32,
"count": 5
}
]
}
Analytics MTTD
Returns Mean Time To Detect per day, computed from Issue timestamps
curl --request GET \
--url {scheme}://{host}:{port}/{basePath}/analytics/mttd \
--header 'X-API-Key: <api-key>'import requests
url = "{scheme}://{host}:{port}/{basePath}/analytics/mttd"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('{scheme}://{host}:{port}/{basePath}/analytics/mttd', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "62437",
CURLOPT_URL => "{scheme}://{host}:{port}/{basePath}/analytics/mttd",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "{scheme}://{host}:{port}/{basePath}/analytics/mttd"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("{scheme}://{host}:{port}/{basePath}/analytics/mttd")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("{scheme}://{host}:{port}/{basePath}/analytics/mttd")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"apiVersion": "v1",
"kind": "MTTDMetrics",
"metadata": {
"totalCount": 3,
"page": 1,
"pageSize": 3
},
"items": [
{
"date": "2026-03-17",
"value": 45,
"count": 3
},
{
"date": "2026-03-18",
"value": 28,
"count": 8
},
{
"date": "2026-03-19",
"value": 32,
"count": 5
}
]
}
2026-03-01T00:00:00Z). An unparseable value is ignored.viewer. Covers every namespace.
For each Issue created in the window, except Issues induced by a chaos experiment (label platform.chatcli.io/source: chaos-experiment), the detection time is status.detectedAt − metadata.creationTimestamp (0 when detectedAt is empty or earlier). Issues are grouped by the day they were created (YYYY-MM-DD, ascending). In each item, value is the mean in seconds and count the number of Issues that day.
Because an Issue is created when the operator reacts to an anomaly, this measures the gap between the Issue and its detection mark, not the time from the underlying fault to detection. Values near 0 are expected.
{
"apiVersion": "v1",
"kind": "MTTDMetrics",
"metadata": {
"totalCount": 3,
"page": 1,
"pageSize": 3
},
"items": [
{
"date": "2026-03-17",
"value": 45,
"count": 3
},
{
"date": "2026-03-18",
"value": 28,
"count": 8
},
{
"date": "2026-03-19",
"value": 32,
"count": 5
}
]
}
Authorizations
API key sent in the X-API-Key header. Keys are read from the Secret chatcli-operator-secrets, key api-keys (fallback: ConfigMap chatcli-operator-config, key api-keys) in the operator namespace, as a YAML list of {key, role, name, description} (name is the identity recorded on approval decisions); the operator chart creates it only with apiKeys.create: true. Roles: viewer < operator < admin — any other role string is denied everywhere. Changes are picked up within about 30 seconds; deleting both the Secret and the ConfigMap revokes every key. With no keys configured every /api/ call returns 401, unless CHATCLI_OPERATOR_DEV_MODE=true, which grants admin without a key (development only).
Query Parameters
Start of the time range (RFC3339). Unparseable values are ignored.
"2026-03-01T00:00:00Z"
End of the time range (RFC3339). Unparseable values are ignored.
"2026-03-19T23:59:59Z"