Skip to content

Read usage across tenants

View as Markdown
GET
/v1/system/usage
curl --request GET \
--url 'https://example.com/v1/system/usage?from=2026-04-15T12%3A00%3A00Z&to=2026-04-15T12%3A00%3A00Z' \
--header 'Authorization: Bearer <token>'

Usage records aggregated by the dimensions in groupBy: each group’s count, the sum of its quantities, and its first and last times. Never a record’s content. Records are counted when the counted thing happens, exactly once (the kinds kind lists, each run with its class). watermark: every record at or before it is in the answer, so re-read a window until its watermark is past the window’s end; every page of one read carries the same watermark. A test tenant’s usage, and one whose setup is pending, are never in the answer, even when tenantId names one. Takes system:usage:read; the access record marks a read across every tenant, or of the one named. A runtime that doesn’t count usage (KINDGI_USAGE_METERING unset or off) answers 404.

from
required
string format: date-time

Records at or after this time: a time with a zone, such as 2026-11-01T00:00:00Z.

to
required
string format: date-time

Records before this time. At most 92 days after from.

groupBy
string

Comma-separated, each once: tenant, project, agent, kind, class, state, day (the UTC date). Default tenant,kind.

kind
string

Only these kinds, comma-separated (run.started, run.ended, approval.requested, approval.decided, llm.inference, llm.embedding, project.production).

class
string

Only these run classes, comma-separated (production, staging, dry-run, replay, eval, child).

state
string

Only these states, comma-separated: on, off (project.production); completed, failed, cancelled (run.ended); escalation (approval.requested, an escalation’s next tier); approved, rejected, withdrawn, escalated, expired (approval.decided).

limit
integer
>= 1 <= 10000

Groups a page: 1 to 10000, default 1000.

cursor
string

The previous page’s nextCursor.

tenantId
string format: uuid

Only this tenant.

A page of groups.

Media typeapplication/json
object
data
required
Array<object>

One group: the dimensions it’s grouped by (only those), then its count, the sum of its quantities, and its first and last times.

object
tenantId

The tenant, on the operator’s read across tenants.

string format: uuid
projectId

Absent for a record with no project; a tenant admin’s read includes those, and the CLI shows them as (none).

string
agentId

Absent for a record with no agent; the CLI shows those as (none).

string
kind

What was counted: run.started, run.ended, approval.requested, approval.decided, llm.inference, llm.embedding, project.production. A runtime may count more kinds later: show one you don’t know as it is.

string
class

The class of the run a record counts or belongs to, fixed when the run was created: production, staging, dry-run, replay, eval, child. A record with no counted run has none. A runtime may add classes later: show one you don’t know as it is.

string
state

The record’s state, by kind: on, off (project.production); completed, failed, cancelled (run.ended); escalation (approval.requested, an escalation’s next tier); approved, rejected, withdrawn, escalated, expired (approval.decided). Other kinds have none.

string
day

The UTC date, YYYY-MM-DD.

string
/^\d{4}-\d{2}-\d{2}$/
count
required

Records in the group.

integer
quantity
required

The sum of their quantities: milliseconds for run.ended, tokens for model calls, and 1 for every other record, so a group of those has a quantity equal to its count.

number
firstAt
required
string format: date-time
lastAt
required
string format: date-time
watermark
required

Every record at or before this time is in the answer; a later one may not be yet. Re-read a window until its watermark is past the window’s end. Every page of one read carries the same watermark.

string format: date-time
nextCursor

More groups: pass it as cursor. Absent on the last page.

string
Examplegenerated
{
"data": [
{
"tenantId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"projectId": "example",
"agentId": "example",
"kind": "example",
"class": "example",
"state": "example",
"day": "example",
"count": 1,
"quantity": 1,
"firstAt": "2026-04-15T12:00:00Z",
"lastAt": "2026-04-15T12:00:00Z"
}
],
"watermark": "2026-04-15T12:00:00Z",
"nextCursor": "example"
}

A window, filter, limit or cursor this route can’t read (bad-input), naming which.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

Missing / malformed / expired / revoked bearer token.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

A tenant’s key or session (system-principal-required), or a system key without system:usage:read (system-scope-missing).

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

See also