Skip to main content
Every meaningful action against your platform lands in the activity log: settlements created, compliance decisions taken, team roles changed, webhook endpoints edited, secrets rotated, and every machine-to-machine API call. Read it with 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>:
The first segment is the category, and it is the value the 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’s description 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 with 401 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 an admin. 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.