API reference¶
KubeManta's agent is a REST API, and everything the dashboard does it does through that API. Anything you can do in the UI you can script — subject to the limits below, which exist so that a leaked key cannot become a cluster takeover.
The schema is served by the product¶
The agent publishes its own OpenAPI schema, so the authoritative endpoint list is the one running in your cluster rather than a page that can drift from it:
| Path | What it is |
|---|---|
/api/agent/openapi.json |
OpenAPI 3 schema — every route, parameter and response model |
/api/agent/docs |
Swagger UI — browse and try requests interactively |
Both require authentication, like every other route. In a browser you are already
signed in, so https://<your-host>/api/agent/docs just works. From a script:
curl -s https://kubemanta.example.com/api/agent/openapi.json \
-H "X-API-Key: $KM_API_KEY" | jq '.paths | keys[]'
Generate a client
The schema is ordinary OpenAPI 3, so openapi-generator, oapi-codegen and
friends will produce a typed client from it directly.
Authentication¶
Two credential kinds reach the API, and they are not equivalent.
Session cookie (km_session) — what the dashboard uses. HMAC-signed, sliding idle
expiry, absolute 7-day ceiling. Not intended for scripts.
Per-user API key (X-API-Key) — what automation uses.
Keys look like km_<prefix>_<secret>. Only a SHA-256 hash is stored, so the secret is
shown once at creation and cannot be recovered — reissue rather than hunt for it.
Creating a key¶
- For yourself: the account menu → API keys, or
POST /me/api-keys. - For another user (admin): Admin → Users → pick the user → API keys → Create.
A key belongs to a user, and that is the point: it carries its owner's role, and the role is re-read from the database on every request. Demote the owner to viewer and the key immediately acts as a viewer; deactivate them, revoke the key, or let it expire and the next call gets a 401. There is no shared secret and nothing to redeploy.
The old shared key is gone
security.apiKey was removed. It was unattributable — every caller looked like the
same root principal — could not be revoked without a redeploy, and bypassed the seat
model. If you are following an older guide that sets it, that value does nothing.
Attribution¶
Key requests are audited as <username>@<ip>, so a shared key is visible as one account
calling from many addresses. That is deliberate: it makes credential sharing a thing you
can see in Activity rather than a thing you have to trust people about.
What an API key cannot do¶
A key is not a full stand-in for its owner. Identity, billing and escalation surfaces
refuse keys outright — even when the owner is an admin — and answer
403 {"error": "api_key_forbidden"}.
| Refused | Why |
|---|---|
/admin/users*, /admin/api-keys*, /me/api-keys* |
A key must never mint or revoke keys, or change who is an admin |
/admin/sso*, /admin/sessions*, /admin/license* |
Identity and licensing are not scriptable with a key |
/settings/ai |
A GET returns your LLM provider key unmasked |
/admin/ai-guardrails, /admin/access-control (writes) |
The AI kill switch, redaction, and IP bans. Reads are allowed |
/admin/*, /settings/* (writes) |
Includes expert mode — the toggle that enables cluster-admin delegation |
/kubemanta/db/* |
The purge would erase the record of the key's own use |
/helm/configs, /builder/bundles (reads) |
These decrypt before returning; a saved config can hold credentials |
/terminal/* and the terminal WebSocket |
Refused by credential kind, whatever the owner's role |
The reasoning is one scenario: a leaked key should not be able to enable expert mode, apply a manifest as cluster-admin, disable redaction, ban the operators, and then purge the audit trail — each step is individually plausible, and together they are a takeover that needs no browser and no shell.
Everything not listed is reachable and inherits the owner's role. That default-allow
shape is why the list is enforced as prefix rules rather than an enumeration: a route
added tomorrow under /admin/ is refused the day it lands, not the day somebody
remembers to add it.
Base path¶
Through the UI (the normal case), every agent route is under /api/agent:
Talking to the agent Service directly inside the cluster, drop that prefix:
Worked examples¶
export KM=https://kubemanta.example.com/api/agent
export KM_API_KEY=km_a1b2c3d4_…
# Namespaces you can see
curl -s "$KM/namespaces" -H "X-API-Key: $KM_API_KEY" | jq '.namespaces[].name'
# Pods in one namespace
curl -s "$KM/namespaces/prod/pods" -H "X-API-Key: $KM_API_KEY" | jq '.pods[].name'
# Current security posture (grade, severity counts, coverage)
curl -s "$KM/security/posture?namespace=prod" -H "X-API-Key: $KM_API_KEY" \
| jq '{grade, score, by_severity, workloads_covered}'
# Findings, newest scan, worst first — paginated
curl -s "$KM/security/findings?page=1&page_size=50&sort=severity" \
-H "X-API-Key: $KM_API_KEY" | jq '.total, .rows[0]'
# Export the current findings as CSV
curl -s "$KM/security/findings/export?format=csv" -H "X-API-Key: $KM_API_KEY" > findings.csv
Rate limits and errors¶
| Status | Meaning |
|---|---|
401 |
No credential, an expired session, or a revoked/expired key |
402 |
The endpoint needs a license tier you do not have — the body carries feature and an upgrade hint |
403 |
Authenticated but not permitted: a viewer calling a write, a denied IP, or api_key_forbidden |
429 |
Login lockout, or the discovery rate limit |
503 |
The agent is shedding load (in-flight cap) — retry with the Retry-After header |
Errors are JSON objects with an error field where the distinction matters
programmatically, so branch on that rather than parsing prose.
See also¶
- MCP server — the read-only tool surface for AI agents, same key
- Security model — why keys are per-user
- Access control — IP allow/deny and auto-ban