Read usage across tenants
const url = 'https://example.com/v1/system/usage?from=2026-04-15T12%3A00%3A00Z&to=2026-04-15T12%3A00%3A00Z';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Records at or after this time: a time with a zone, such as 2026-11-01T00:00:00Z.
Records before this time. At most 92 days after from.
Comma-separated, each once: tenant, project, agent, kind, class, state, day (the UTC date). Default tenant,kind.
Only these kinds, comma-separated (run.started, run.ended, approval.requested, approval.decided, llm.inference, llm.embedding, project.production).
Only these run classes, comma-separated (production, staging, dry-run, replay, eval, child).
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).
Groups a page: 1 to 10000, default 1000.
The previous page’s nextCursor.
Only this tenant.
Responses
Section titled “Responses”A page of groups.
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
The tenant, on the operator’s read across tenants.
Absent for a record with no project; a tenant admin’s read includes those, and the CLI shows them as (none).
Absent for a record with no agent; the CLI shows those as (none).
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.
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.
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.
The UTC date, YYYY-MM-DD.
Records in the group.
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.
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.
More groups: pass it as cursor. Absent on the last page.
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.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Missing / malformed / expired / revoked bearer token.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
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).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}