> ## Documentation Index
> Fetch the complete documentation index at: https://docs.keystoneos.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Activity Log

> How activity event types are named, which categories you can filter on, and what each category covers.

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}`.

```bash theme={null}
curl "https://api.keystoneos.xyz/v1/activity?category=webhook&limit=20" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

## Event type naming

Event types are dot-delimited and read `<category>.<event>`:

```
settlement.created
webhook.secret_rotated
security.auth_failed
```

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

| Category      | Covers                                                                                                                |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| `settlement`  | Settlement lifecycle: creation, state changes, on-chain registration and execution, timeouts, escrow outcomes         |
| `instruction` | Paired instructions submitted, matched, cancelled                                                                     |
| `compliance`  | Screening outcomes and compliance decisions                                                                           |
| `session`     | Session tokens minted and revoked                                                                                     |
| `member`      | Team membership: invitations, role changes, removals                                                                  |
| `platform`    | Your platform record: settings updated, onboarding completed                                                          |
| `environment` | Your environments: created, updated, deactivated, client secret rotated                                               |
| `webhook`     | Your webhook endpoints: created, updated, deleted, secret rotated                                                     |
| `security`    | Your IP allowlist, and requests refused with `401` or `403` after resolving to your platform (`security.auth_failed`) |
| `api`         | Machine-to-machine API calls (`api.read`, `api.write`, `api.error`)                                                   |
| `admin`       | Actions KeyStone operators took on your platform                                                                      |

`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

| Parameter                       | Effect                                                                |
| ------------------------------- | --------------------------------------------------------------------- |
| `category`                      | Exact match on the category, per the table above                      |
| `type`                          | Exact match on the full event type                                    |
| `search`                        | Substring match across description, type, actor and resource id       |
| `resource_type` / `resource_id` | The object the event acted on                                         |
| `environment_id`                | Narrow to one environment within what your credential can already see |
| `date_from` / `date_to`         | Inclusive time bounds                                                 |

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.

| Previously                         | Now                           |
| ---------------------------------- | ----------------------------- |
| `admin.platform_updated`           | `platform.updated`            |
| `admin.environment_created`        | `environment.created`         |
| `admin.environment_updated`        | `environment.updated`         |
| `admin.environment_deactivated`    | `environment.deactivated`     |
| `admin.environment_secret_rotated` | `environment.secret_rotated`  |
| `admin.webhook_endpoint_created`   | `webhook.endpoint_created`    |
| `admin.webhook_endpoint_updated`   | `webhook.endpoint_updated`    |
| `admin.webhook_endpoint_deleted`   | `webhook.endpoint_deleted`    |
| `admin.webhook_secret_rotated`     | `webhook.secret_rotated`      |
| `admin.allowed_ip_created`         | `security.allowed_ip_created` |
| `admin.allowed_ip_deleted`         | `security.allowed_ip_deleted` |

`admin.platform_updated`, `admin.environment_updated`, and `admin.environment_deactivated` still exist for the operator-initiated versions of the same changes.
