curl --request GET \
--url {scheme}://{host}:{port}/{basePath}/analytics/capacity \
--header 'X-API-Key: <api-key>'import requests
url = "{scheme}://{host}:{port}/{basePath}/analytics/capacity"
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/capacity', 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/capacity",
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/capacity"
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/capacity")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("{scheme}://{host}:{port}/{basePath}/analytics/capacity")
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": "CapacityForecastList",
"metadata": {
"totalCount": 2,
"page": 1,
"pageSize": 2
},
"items": [
{
"Resource": {
"kind": "Deployment",
"name": "payment-service",
"namespace": "production"
},
"CurrentUsage": {
"CPUMillicores": 400,
"MemoryBytes": 912680550
},
"Limits": {
"CPUMillicores": 500,
"MemoryBytes": 1073741824
},
"UsagePercentage": {
"CPU": 80,
"Memory": 85
},
"Trend": {
"CPUTrendPerDay": 0,
"MemoryTrendPerDay": 0,
"Direction": "insufficient_history"
},
"Forecast": {
"CPUExhaustionDate": null,
"MemoryExhaustionDate": null,
"DaysUntilCPUExhaustion": 0,
"DaysUntilMemoryExhaustion": 0,
"Recommendation": "Requests are above 80% of limits. Consider increasing limits proactively."
},
"IncidentCorrelation": {
"IncidentsInWindow": 9,
"ResourceRelatedIncidents": 4,
"ResourceIsBottleneck": true
},
"UsageSource": "requests",
"HistoryAvailable": false,
"Urgency": "plan"
},
{
"Resource": {
"kind": "Deployment",
"name": "api-gateway",
"namespace": "production"
},
"CurrentUsage": {
"CPUMillicores": 250,
"MemoryBytes": 268435456
},
"Limits": {
"CPUMillicores": 1000,
"MemoryBytes": 536870912
},
"UsagePercentage": {
"CPU": 25,
"Memory": 50
},
"Trend": {
"CPUTrendPerDay": 0,
"MemoryTrendPerDay": 0,
"Direction": "insufficient_history"
},
"Forecast": {
"CPUExhaustionDate": null,
"MemoryExhaustionDate": null,
"DaysUntilCPUExhaustion": 0,
"DaysUntilMemoryExhaustion": 0,
"Recommendation": "No usage history is collected, so no trend or exhaustion date can be projected; requests are within limits."
},
"IncidentCorrelation": {
"IncidentsInWindow": 9,
"ResourceRelatedIncidents": 1,
"ResourceIsBottleneck": false
},
"UsageSource": "requests",
"HistoryAvailable": false,
"Urgency": "none"
}
]
}
Analytics Capacity
Returns a requests-versus-limits capacity report for Deployments that had incidents
curl --request GET \
--url {scheme}://{host}:{port}/{basePath}/analytics/capacity \
--header 'X-API-Key: <api-key>'import requests
url = "{scheme}://{host}:{port}/{basePath}/analytics/capacity"
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/capacity', 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/capacity",
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/capacity"
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/capacity")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("{scheme}://{host}:{port}/{basePath}/analytics/capacity")
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": "CapacityForecastList",
"metadata": {
"totalCount": 2,
"page": 1,
"pageSize": 2
},
"items": [
{
"Resource": {
"kind": "Deployment",
"name": "payment-service",
"namespace": "production"
},
"CurrentUsage": {
"CPUMillicores": 400,
"MemoryBytes": 912680550
},
"Limits": {
"CPUMillicores": 500,
"MemoryBytes": 1073741824
},
"UsagePercentage": {
"CPU": 80,
"Memory": 85
},
"Trend": {
"CPUTrendPerDay": 0,
"MemoryTrendPerDay": 0,
"Direction": "insufficient_history"
},
"Forecast": {
"CPUExhaustionDate": null,
"MemoryExhaustionDate": null,
"DaysUntilCPUExhaustion": 0,
"DaysUntilMemoryExhaustion": 0,
"Recommendation": "Requests are above 80% of limits. Consider increasing limits proactively."
},
"IncidentCorrelation": {
"IncidentsInWindow": 9,
"ResourceRelatedIncidents": 4,
"ResourceIsBottleneck": true
},
"UsageSource": "requests",
"HistoryAvailable": false,
"Urgency": "plan"
},
{
"Resource": {
"kind": "Deployment",
"name": "api-gateway",
"namespace": "production"
},
"CurrentUsage": {
"CPUMillicores": 250,
"MemoryBytes": 268435456
},
"Limits": {
"CPUMillicores": 1000,
"MemoryBytes": 536870912
},
"UsagePercentage": {
"CPU": 25,
"Memory": 50
},
"Trend": {
"CPUTrendPerDay": 0,
"MemoryTrendPerDay": 0,
"Direction": "insufficient_history"
},
"Forecast": {
"CPUExhaustionDate": null,
"MemoryExhaustionDate": null,
"DaysUntilCPUExhaustion": 0,
"DaysUntilMemoryExhaustion": 0,
"Recommendation": "No usage history is collected, so no trend or exhaustion date can be projected; requests are within limits."
},
"IncidentCorrelation": {
"IncidentsInWindow": 9,
"ResourceRelatedIncidents": 1,
"ResourceIsBottleneck": false
},
"UsageSource": "requests",
"HistoryAvailable": false,
"Urgency": "none"
}
]
}
to − from when both are valid, otherwise 7 days; the window always ends at the current time.viewer. One forecast is built for each distinct resource referenced by an Issue (any Issue, not only those in the window). The planner reads it as a Deployment of that name; resources that are not Deployments, or that no longer exist, are skipped silently.
Forecast Fields
| Field | Description |
|---|---|
Resource | The resource the Issues point at (kind, name, namespace) |
CurrentUsage | Requests of the first container, used as a proxy for usage (CPUMillicores, MemoryBytes); not live metrics |
Limits | Limits of the first container |
UsagePercentage | CurrentUsage / Limits × 100 for CPU and Memory |
Trend | Direction is insufficient_history and the per-day slopes are 0: the operator keeps no usage history to fit a trend |
Forecast | A Recommendation string; the exhaustion dates are null and the day counts 0, since no exhaustion date is projected |
IncidentCorrelation | Issues in the namespace inside the window, how many target this resource, and ResourceIsBottleneck (more than 2) |
UsageSource | Where CurrentUsage comes from: always requests |
HistoryAvailable | Always false: no usage samples are collected |
Urgency | plan when requests reach 80% of limits or the resource is a bottleneck, none otherwise |
Resource keeps lowercase inner keys). Absent exhaustion dates are null and absent day counts are 0.{
"apiVersion": "v1",
"kind": "CapacityForecastList",
"metadata": {
"totalCount": 2,
"page": 1,
"pageSize": 2
},
"items": [
{
"Resource": {
"kind": "Deployment",
"name": "payment-service",
"namespace": "production"
},
"CurrentUsage": {
"CPUMillicores": 400,
"MemoryBytes": 912680550
},
"Limits": {
"CPUMillicores": 500,
"MemoryBytes": 1073741824
},
"UsagePercentage": {
"CPU": 80,
"Memory": 85
},
"Trend": {
"CPUTrendPerDay": 0,
"MemoryTrendPerDay": 0,
"Direction": "insufficient_history"
},
"Forecast": {
"CPUExhaustionDate": null,
"MemoryExhaustionDate": null,
"DaysUntilCPUExhaustion": 0,
"DaysUntilMemoryExhaustion": 0,
"Recommendation": "Requests are above 80% of limits. Consider increasing limits proactively."
},
"IncidentCorrelation": {
"IncidentsInWindow": 9,
"ResourceRelatedIncidents": 4,
"ResourceIsBottleneck": true
},
"UsageSource": "requests",
"HistoryAvailable": false,
"Urgency": "plan"
},
{
"Resource": {
"kind": "Deployment",
"name": "api-gateway",
"namespace": "production"
},
"CurrentUsage": {
"CPUMillicores": 250,
"MemoryBytes": 268435456
},
"Limits": {
"CPUMillicores": 1000,
"MemoryBytes": 536870912
},
"UsagePercentage": {
"CPU": 25,
"Memory": 50
},
"Trend": {
"CPUTrendPerDay": 0,
"MemoryTrendPerDay": 0,
"Direction": "insufficient_history"
},
"Forecast": {
"CPUExhaustionDate": null,
"MemoryExhaustionDate": null,
"DaysUntilCPUExhaustion": 0,
"DaysUntilMemoryExhaustion": 0,
"Recommendation": "No usage history is collected, so no trend or exhaustion date can be projected; requests are within limits."
},
"IncidentCorrelation": {
"IncidentsInWindow": 9,
"ResourceRelatedIncidents": 1,
"ResourceIsBottleneck": false
},
"UsageSource": "requests",
"HistoryAvailable": false,
"Urgency": "none"
}
]
}
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
Kubernetes namespace. Empty means all namespaces.
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"