Two read-only projections over control’s own tables. Both take a closed UTC window, both page on an opaque cursor, and both validate their response against a published schema before returning it — a body that does not match the contract is a 503, not a partial answer.

Authentication

Authorization: Bearer <session token>, verified as /api-reference/overview describes. Usage needs the usage.read action and audit needs audit.read; both are granted to every role including viewer. A non-member gets 404 workspace not found. Query errors answer 422 with a field-shaped detail:
A storage or validation failure answers 503 Observe read model unavailable.

Usage summary

GET /api/v1/workspaces/{workspace_id}/usage Aggregate usage over a window, grouped one way.
string
required
Start of the UTC window, 1–64 characters. Inclusive.
string
required
End of the UTC window, 1–64 characters. Exclusive.
string
default:"kind"
One of kind, key, model, provider, tool.
integer
default:"50"
1–200 groups.
string
Opaque group cursor from a previous next_cursor, at most 2048 characters. Bound to the same group_by.

Response

object
{from, to}, echoed as parsed.
object
Aggregates over the whole window: event_count, llm_requests, unpriced_count, usd_micros, tokens_in, tokens_out, tokens_cached, tokens_reasoning, tool_calls, partial_count, failure_count. Every value is an exact integer — sums cross the JSON boundary without a float conversion.
integer
Sums only events whose pricing_status is known. Events priced unknown are counted in unpriced_count and contribute nothing to spend, so an unpriced call is never quietly billed as free.
array
{value, totals} per group, where value is the grouped column and may be null. null sorts last.
array
Per-key budget posture for the current calendar month, read from the gateway’s spend counters.
string
The most recent ingestion timestamp inside the window, or null when the window is empty.
string
complete, or gap_detected when the workspace has a recorded usage ingestion gap. A summary that might be missing events says so rather than looking whole.
string
Cursor for the next page of groups, or null.
See /billing/usage for how these events are metered and /billing/metering for what is counted.

Audit events

GET /api/v1/workspaces/{workspace_id}/audit Operator actions in the workspace, newest first.
string
required
Start of the UTC window, 1–64 characters.
string
required
End of the UTC window, 1–64 characters.
string
Filter to one action, at most 64 characters. Must be in the closed OperatorAuditAction vocabulary — an unknown value is 422 unknown audit action rather than an empty list.
string
Filter to one operator, at most 64 characters.
string
Filter to one target id, at most 128 characters.
integer
default:"50"
1–200.
string
Opaque time cursor from a previous next_cursor, at most 2048 characters.

Actions

Rows whose stored action is outside this vocabulary are excluded from the listing entirely.

Response

object
{user_id, email}. The email is dropped to null if it exceeds 320 characters.
string
The acted-on id, or null when it exceeds 256 characters.
object
A filtered subset of the stored detail. Only these keys survive: agent_id, bundle_hash, connection_id, env, key_id, last4, policy_id, prefix, provider_id, publication_state, qualifier_id, reason, revision_id, snapshot_version, source, status, tier. At most 16 keys, and each value must be a string of at most 256 characters, a boolean, an int64, or a list of at most 16 such strings. Everything else is dropped, so an audit detail can never carry an unreviewed value out.
string
Cursor for the next page, or null.
See /observability/audit-log for what each action records.