GET /v1/activity, or a single entry with GET /v1/activity/{log_id}.
Event type naming
Event types are dot-delimited and read<category>.<event>:
category filter matches. The rest names the event within that category, in snake_case. An entry’s category field is always the first segment of its type, so the two filters never disagree.
Categories
admin is reserved for interventions by KeyStone staff, never for your own configuration changes. Filtering ?category=admin therefore answers “what did KeyStone do to my platform”, and filtering by a resource category answers “what did my team do to my webhooks / environments / IP allowlist”. Operator events namespace one level deeper, for example admin.settlement.rollback; the category is still the first segment.
What a description holds
An entry’sdescription is written for a human reading the list, and it never
carries a credential. A URL appears as its origin alone, so a webhook endpoint
registered at https://your-platform.com/webhooks/keystone?token=... is
recorded as Webhook endpoint created for https://your-platform.com. Many
receivers authenticate by the query string or by the path, and the entry is
durable, so the value is narrowed before the row is written.
Nothing is lost in identifying what an entry is about: resource_type and
resource_id name the exact object, so a webhook entry carries the endpoint
id, and GET /v1/platforms/me/webhooks/{endpoint_id} returns that endpoint’s
URL in full to you.
Refused requests
A request refused with401 or 403 after it resolved to your platform appears as
security.auth_failed, severity warning. The entry carries the refusal’s code (for
example INSUFFICIENT_SCOPES, IP_NOT_ALLOWED, SESSION_WRITE_DENIED) in
detail.reason, the kind of credential presented in detail.credential (bearer,
signature, other_scheme, none), the route template in path, and the client
address in ip_address. The credential itself is never recorded, in any form: not the
value, not a fragment, not a digest of it.
A request that presented no credential your platform owns leaves no entry here, because
nothing tied it to your platform. Those refusals are recorded in KeyStone’s own
operational logs instead.
Who acted
actor_type names the class of credential behind an entry and actor_id the identifier
that names one of that class: m2m with the credential’s subject (<client_id>@clients), user with the person’s subject,
admin for a KeyStone operator, and session with the session token’s id for anything a
browser-delivered token did. A session token’s actor_id is the token_id you received
when you minted it, so an entry can be traced to the token and the token revoked.
Filtering
Environment-bound credentials see their own environment. Platform-wide credentials see every environment plus entries that belong to none, and can use
environment_id to narrow within that.
Renamed event types (2026-08-06)
Eleven event types raised by your own configuration changes previously carried anadmin. prefix, which filed them under the admin category alongside KeyStone operator actions. They now carry their resource domain. Existing entries were rewritten to the new names, so no query needs to handle both.
admin.platform_updated, admin.environment_updated, and admin.environment_deactivated still exist for the operator-initiated versions of the same changes.