# List audit events (admin)

`GET /v1/admin/orgs/{slug}/audit-events`

Returns one page of audit records for the organization, newest first,
using opaque cursor (keyset) pagination.

**The time window is bounded, and that is the point.** Records are stored
in monthly partitions on Postgres, so a query that names a window lets the
database discard every partition outside it; an unbounded one would scan
the entire retention period. Omitting both `from` and `to` therefore means
"the last 30 days" rather than "everything", and a window wider than 90
days — the bound `x-window` declares — is rejected with a 400; narrow it
and walk the pages.

Filters compose as a conjunction. The axes a detector keys on are each
answered from an index: one value of `reason`, `outcome`, `operation`,
`actor_type`, `client_net` or `client_id`, and the set
`outcome=failure,denied,error`, every refusal and fault. Several values of
one filter narrow the window as it is read in time order instead. There
is no free-text search: the fields worth searching — `action`,
`category`, `outcome`, `severity`, `reason`, `actor_type` — are closed
vocabularies with their own parameters, so a substring scan over an
append-only table this size is never the answer.

`client_id` finds a client's records whether the server resolved them to
the client or the caller only named it — a sign-in form's `client_id`, an
identifier a client authentication presented that no client registered —
and each record says which, by carrying it as `client_id` or as
`claimed_client_id`.

This is a page through a window of time, newest first, and not a way to
follow the log as it grows: a record can become visible after records
that occurred later, so a walk by time skips it. Follow the log with
`adminTailAuditEvents` instead.

Reading the audit log is itself audited, whatever the configured level:
the record names whose trail was read — the principal, target, client,
session or network filtered for — and whether platform records were in
view. A query refused for its filters, window or cursor is recorded too.

Requires the `nauthera.audit:read` scope and the `audit.read` permission.
A caller is confined to its own org unless a platform role held in the
main org admits it to every org. A caller who also holds `platform.audit`
additionally sees platform-scoped records (process startup, retention,
audit-pipeline failures), which have no owning tenant and are never shown
to one.

## Authorization

- [Bearer token](https://www.nauthera.io/docs/v1/api#auth-bearerAuth) (`bearerAuth`) and [OAuth 2.0](https://www.nauthera.io/docs/v1/api#auth-oauth2) (`oauth2`) with scope `nauthera.audit:read`

Authorization header: The access token, as `Bearer <token>` (RFC 6750 §2.1) or, for a sender-constrained token, `DPoP <token>` (RFC 9449 §7.1). A token minted with a `cnf.jkt` confirmation is only accepted together with a `DPoP` proof signed by that key, whichever of the two schemes carries it.

Requires the permission [`audit.read`](https://www.nauthera.io/docs/v1/api#permission-audit.read): Read an organization's audit log.

This endpoint is rate limited; it answers `429` when a limit is hit.

## Path parameters

- `slug` (string, required): The organization slug. It names the TENANT OF RECORD for the operation: the rows it reads and writes, the policy its secrets are derived under, and the organization its audit record is filed against all come from this organization. A token is admitted only through its own slug, except a main-org token, which may be admitted through any — so cross-tenant administration is performed through the TARGET tenant's slug, never by naming its resources through another. A token reaching for a slug it may not be admitted through is refused identically whether that organization exists or not. Constraints: 1–64 characters.

## Query parameters

- `limit` (integer, optional): Maximum number of items to return in one page (1–200; default 50). A value outside that range is rejected with a 400. It may be changed freely between pages of the same walk — a cursor marks a position, not a page number. Default: `50`. Constraints: between 1 and 200.
- `sort` (string, optional): Ordering for the listing: a comma-separated list of fields, most significant first, a leading `-` marking a descending term. For example `sort=last_name,-first_name` sorts by last name ascending, then by first name descending. Constraints: at most 256 characters; pattern `^-?[a-z][a-z0-9_]*(,-?[a-z][a-z0-9_]*)*$`. Sortable fields: `id`, `occurred_at`. Prefix a field with `-` to sort descending.

  Which fields a given endpoint accepts is listed in its `x-sort-fields`
  extension; naming anything else is a 400. A field may appear only once.

  The server always appends the resource's primary key as a final term, so
  the ordering is TOTAL — rows that tie on every field you named still have
  a defined relative order, without which a page could skip or repeat them.
  The full ordering, including that appended term, is echoed as `page.sort`.

  Send it on the FIRST request of a walk only: every cursor records the
  ordering it was issued under, so it carries forward automatically, and a
  `sort` that contradicts the supplied `cursor` is rejected with a 400
  rather than silently reordering mid-walk.
- `cursor` (string, optional): Opaque keyset cursor identifying where a page begins. Omit it for the first page; afterwards pass the `next_cursor` (or `prev_cursor`) value from the previous response verbatim — the token itself records which direction it steps in, so there is no separate direction parameter. Constraints: at most 1024 characters.

  The token is server-defined and MUST be treated as opaque: it is bound
  to the listing and sort order that issued it, and a cursor that is
  malformed, tampered with, or belongs to a different listing or order is
  rejected with a 400 rather than silently paging through the wrong set.
- `from` (string (date-time), optional): Inclusive start of the window (RFC3339). Defaults to 30 days before `to`. The window from `from` to `to` may span at most 90 days.
- `to` (string (date-time), optional): Exclusive end of the window (RFC3339). Defaults to now. The window from `from` to `to` may span at most 90 days.
- `action` (array of strings, optional): Action patterns, comma-separated. `*` and `?` are wildcards and do not stop at a dot, so `user.*` covers `user.password.changed` as well as `user.created`. A pattern matching no known action simply returns nothing, and an empty element is ignored. With no pattern, every action but `system.audit.checkpoint` is returned: the hash chain's checkpoint records, which the verifier reads, come back only to a pattern that matches them. Constraints: at most 32 items; each item: at most 128 characters; each item: pattern `^[a-z0-9_.*?-]*$`.
- `category` (array of enums, optional): Categories, comma-separated. A record is matched by the category it was written under. One of: `"authentication"`, `"session"`, `"mfa"`, `"token"`, `"user"`, `"organization"`, `"client"`, `"role"`, `"admin"`, `"audit"`, `"system"`. Constraints: at most 16 items.
- `outcome` (array of enums, optional): Outcomes, comma-separated. `failure,denied,error`, in any order, is every refusal and fault: the base query of most detectors, which has an index of its own. One of: `"success"`, `"failure"`, `"denied"`, `"error"`. Constraints: at most 16 items.
- `severity` (array of enums, optional): Severities, comma-separated. One of: `"info"`, `"notice"`, `"warning"`, `"critical"`. Constraints: at most 16 items.
- `reason` (array of enums, optional): Reasons, comma-separated: the error each refusal answered with, such as `invalid_grant` or `invalid_dpop_proof`. A success carries no reason; ask for successes with `outcome`. One of: `"invalid_credentials"`, `"account_locked"`, `"rate_limited"`, `"mfa_required"`, `"email_verification_required"`, `"invalid_code"`, `"factor_locked"`, `"missing_role"`, `"missing_scope"`, `"cross_org_denied"`, `"not_found"`, `"conflict"`, `"invalid_request"`, `"invalid_client"`, `"invalid_grant"`, `"invalid_redirect_uri"`, `"unauthenticated"`, `"registration_closed"`, `"invalid_target"`, `"invalid_dpop_proof"`, `"repeated_header"`, `"token_expired"`, `"missing_expiry"`, `"audience_mismatch"`, `"unsupported_critical"`, `"unregistered_client"`, `"redirect_uri_required"`, `"pushed_request_required"`, `"invalid_request_uri"`, `"request_uri_client_mismatch"`, `"invalid_request_object"`, `"unavailable"`, `"internal_error"`, `"access_denied"`, `"login_required"`, `"consent_required"`, `"interaction_required"`, `"unsupported_grant_type"`, `"invalid_scope"`, `"unauthorized_client"`, `"unsupported_response_type"`, `"account_selection_required"`, `"invalid_authorization_details"`, `"expired_token"`, `"request_not_supported"`, `"request_uri_not_supported"`, `"unsupported_token_type"`, `"invalid_token"`, `"use_dpop_nonce"`, `"authorization_pending"`, `"slow_down"`, `"invalid_client_metadata"`, `"invalid_software_statement"`, `"unapproved_software_statement"`, `"account_unavailable"`, `"insufficient_user_authentication"`, `"protected_account"`, `"passkey_offered"`, `"password_change_required"`. Constraints: at most 16 items.
- `actor_type` (array of enums, optional): Actor types, comma-separated. `anonymous` is every record made before a caller was identified. One of: `"user"`, `"admin"`, `"client"`, `"system"`, `"anonymous"`. Constraints: at most 8 items.
- `operation` (array of strings, optional): Operations, comma-separated, each matched exactly: the operation a record was made under, as its `operation` field holds it — the operationId as this document spells it, so a record stored by an earlier release, which holds the Go identifier (`GetJWKS`), is found by that spelling. Constraints: at most 16 items; each item: at most 256 characters; each item: pattern `^[A-Za-z0-9_./-]+$`.
- `actor_id` (string, optional): Only records caused by this principal. Constraints: at most 256 characters.
- `target_id` (string, optional): Only records acting on this object. Constraints: at most 256 characters.
- `target_type` (enum, optional): The kind of object an event acted upon. `audit_log` is the log itself, for the events that record reading or exporting it. `consent` is one user's standing agreement with one client for one resource, named by the agreement's id, so every decision about it shares one target. One of: `"user"`, `"organization"`, `"client"`, `"session"`, `"token"`, `"factor"`, `"role"`, `"system"`, `"audit_log"`, `"consent"`.
- `client_ip` (string, optional): Only records from this address. Any spelling of an address matches the one the server stores (`2001:DB8::1` is `2001:db8::1`, and `::ffff:203.0.113.5` is `203.0.113.5`); a value that is not an IP address is rejected with a 400. Constraints: at most 64 characters.
- `client_net` (string, optional): Only records from addresses in this network: an IPv4 `/24` or an IPv6 `/64`, the unit an address rotates within while its owner stays the same. A single address stands for the network it lies in. Any other prefix length is rejected with a 400. Records written before the server kept the network are not matched. Constraints: at most 64 characters.
- `network_zone` (array of strings, optional): Network zones, comma-separated: only records whose client address fell in one of them, as the record's `network_zones` names them. A sign-in records its zones; other records name none and are not matched. A value that is not a lowercase label is rejected with a 400. Constraints: at most 16 items; each item: at most 63 characters.
- `client_id` (string, optional): Only records about this client, whether the server resolved the record to it or the caller only named it. Each record says which: a resolved client is its `client_id`, a named one its `claimed_client_id`. Constraints: at most 256 characters.
- `session_id` (string, optional): Constraints: at most 256 characters.
- `request_id` (string, optional): The `X-Request-ID` of a single request. This and `trace_id` are what a support engineer arrives with. Constraints: at most 256 characters.
- `trace_id` (string, optional): Constraints: at most 64 characters.
- `cross_tenant_only` (boolean, optional): Only actions taken on this organization by an actor from another one. It is the single most useful security view a multi-tenant audit log offers, which is why it is a first-class filter.

## Headers

- `DPoP` (string, optional): DPoP proof JWT (RFC 9449 §4). REQUIRED on a request presenting a DPoP-bound access token, and it must be signed by the key named in the token's `cnf.jkt` and cover this request's method and URI. The proof is read from the request itself rather than from this decoded parameter, so every protected operation is covered whether or not it declares it.

## Responses

### 200 OK

One page of audit records.

Content type: `application/json`

Headers:
- `Cache-Control` = `no-store`

One page of audit records under cursor (keyset) pagination: `items` holds
the page, ordered by the request's `sort`, and `page` says where it sits
in the listing. Follow `page.next_cursor` while `page.has_more` is true.

There is deliberately no total. Counting matching rows on an append-only
table means scanning every partition in the window to answer a question
no client acts on differently — the page itself already reports whether
more follow.

- `items` (array of objects, required): The audit records on this page.

  Each item: One audit record. The server NEVER returns a rendered sentence. It returns the event's `action` plus structured actor/target/metadata, and the client renders the human text by looking up one translation key and interpolating. That is what makes the log translatable, and it is why there is no `message` field to reach for: a stored English sentence cannot be translated, cannot be filtered on, and becomes an accidental API that changes on every refactor. Two fields carry translation keys: `action` (`auditEvents:<action>`) and `reason` (`auditReasons:<reason>`). Both are enumerated vocabularies, so this document alone is enough to build complete translation coverage — which is the reason they are enumerated. That completeness is bought with a compatibility cost, stated on `AuditAction`: a client generated against one version of this document rejects an entire audit page the first time the server emits a value added after that client was built. Regenerate on upgrade. A client that would rather degrade gracefully than fail closed should decode `action` and `reason` as plain strings and keep a fallback label — the values are stable and append-only, so an unrecognized one is always a new value and never a renamed one.
  - `id` (string, required): The record id (UUIDv7, so it is also creation-ordered).
  - `occurred_at` (string (date-time), required): When the event happened.
  - `recorded_at` (string (date-time), required): When the record was persisted. It differs from `occurred_at` by however long the event was buffered, so the gap is the write pipeline's lag.
  - `action` (enum, required): The event type — what was attempted. Append-only: an action is never renamed once shipped, because a rename orphans every stored record and every SIEM rule matching on it. One of: `"admin.access.denied"`, `"audit.counts.read"`, `"audit.log.read"`, `"audit.ranking.read"`, `"auth.account.locked"`, `"auth.email_verification.completed"`, `"auth.email_verification.requested"`, `"auth.invitation.accepted"`, `"auth.password_reset.completed"`, `"auth.password_reset.requested"`, `"auth.rate_limit.triggered"`, `"auth.registration.completed"`, `"auth.sign_in.alerted"`, `"auth.sign_in.completed"`, `"auth.sign_out.completed"`, `"branding.applied"`, `"branding.deleted"`, `"broker_membership.accepted"`, `"broker_membership.clients_set"`, `"broker_membership.ended"`, `"broker_membership.invited"`, `"broker_membership.listed"`, `"client.adopted"`, `"client.applied"`, `"client.configuration.read"`, `"client.configured"`, `"client.deleted"`, `"client.deregistered"`, `"client.listed"`, `"client.name.suspicious"`, `"client.provisioned"`, `"client.read"`, `"client.registered"`, `"client.registration.attempted"`, `"client.secret.issued"`, `"client.usage.read"`, `"email_domain.claimed"`, `"email_domain.listed"`, `"email_domain.lost"`, `"email_domain.removed"`, `"email_domain.verified"`, `"http.cleartext_credential.received"`, `"http.forwarding.ignored"`, `"http.header.repeated"`, `"http.request.screened"`, `"http.request.served"`, `"identity_provider.applied"`, `"identity_provider.deleted"`, `"identity_provider.listed"`, `"ldap_directory.applied"`, `"ldap_directory.deleted"`, `"mfa.email.activated"`, `"mfa.email.challenge_started"`, `"mfa.email.enrollment_started"`, `"mfa.email.revoked"`, `"mfa.email.verified"`, `"mfa.factor.locked"`, `"mfa.passkey.challenge_started"`, `"mfa.passkey.enrollment_started"`, `"mfa.passkey.offer_declined"`, `"mfa.passkey.registered"`, `"mfa.passkey.verified"`, `"mfa.recovery_code.consumed"`, `"mfa.recovery_codes.acknowledged"`, `"mfa.recovery_codes.regenerated"`, `"mfa.request.authenticated"`, `"mfa.status.read"`, `"mfa.totp.activated"`, `"mfa.totp.disabled"`, `"mfa.totp.enrollment_started"`, `"mfa.totp.verified"`, `"oauth.authorization.requested"`, `"oauth.client_authentication.attempted"`, `"oauth.consent.decided"`, `"oauth.consent.revoked"`, `"oauth.device.decided"`, `"oauth.device.redeemed"`, `"oauth.device.requested"`, `"oauth.device.user_code.attempted"`, `"oauth.token.introspected"`, `"oauth.token.issued"`, `"oauth.token.presented"`, `"oauth.token.refreshed"`, `"oauth.token.revoked"`, `"oidc.logout.frontchannel.rendered"`, `"oidc.logout.initiated"`, `"oidc.logout.participant.added"`, `"oidc.logout.participant.skipped"`, `"oidc.logout.token.delivered"`, `"oidc.userinfo.read"`, `"organization.created"`, `"organization.deleted"`, `"organization.listed"`, `"organization.mail.tested"`, `"organization.read"`, `"organization.route.attempted"`, `"organization.snapshot.read"`, `"organization.updated"`, `"resource_problem.listed"`, `"resource_problem.read"`, `"resource_status.reported"`, `"role.granted"`, `"role.revoked"`, `"session.created"`, `"session.history.listed"`, `"session.listed"`, `"session.presented"`, `"session.revoked"`, `"system.admin.bootstrapped"`, `"system.audit.checkpoint"`, `"system.audit.configured"`, `"system.audit.degraded"`, `"system.audit.overflow"`, `"system.audit.partition_created"`, `"system.audit.partition_dropped"`, `"system.audit.recovered"`, `"system.audit.write_delayed"`, `"system.audit.write_failed"`, `"system.debug.accessed"`, `"system.dependency.degraded"`, `"system.dependency.recovered"`, `"system.keys.loaded"`, `"system.migration.applied"`, `"system.session.reaped"`, `"system.signing_key.created"`, `"system.signing_key.dropped"`, `"system.started"`, `"system.stopped"`, `"system.transport.configured"`, `"threats.overview.read"`, `"user.blocked"`, `"user.created"`, `"user.deleted"`, `"user.devices.listed"`, `"user.identities.listed"`, `"user.invitation.sent"`, `"user.listed"`, `"user.password.changed"`, `"user.password_change.required"`, `"user.password_reset.sent"`, `"user.reactivated"`, `"user.read"`, `"user.remembered_browsers.forgotten"`, `"user.suspended"`, `"user.unlocked"`, `"user.updated"`.

    The outcome is deliberately NOT part of the name. `auth.sign_in.completed`
    with `outcome: failure` is a failed sign-in; there is no
    `auth.sign_in.failed`. Encoding it would double this list, the catalog and
    every translation file.

    **Translation key:** the action verbatim in the `auditEvents` namespace —
    `auditEvents:user.password.changed`. Because i18next splits keys on `.`,
    a locale file nests naturally by segment. The catalog endpoint publishes
    the same key as `i18n_key` so a client need not re-derive it.

    **Compatibility:** this is a CLOSED enum, and that has a cost worth
    stating plainly. A client generated against one version of this document
    will reject an entire audit page — not merely one record — the first time
    the server emits an action added after that client was built. Regenerate
    on upgrade, or decode the field as a plain string. It is enumerated
    anyway because the alternative left every consumer unable to know the
    translatable vocabulary without calling the catalog endpoint at runtime.
  - `category` (enum, required): The group an action belongs to, for faceting. Declared per action rather than derived from the action's first segment, because the two do not always agree: `oauth.*` and `oidc.*` events are both "token" work to an operator, and the grouping a UI offers should follow what an operator is looking for rather than how the constant is spelled. One of: `"authentication"`, `"session"`, `"mfa"`, `"token"`, `"user"`, `"organization"`, `"client"`, `"role"`, `"admin"`, `"audit"`, `"system"`.
  - `outcome` (enum, required): How an attempt ended. `denied` is separated from `failure` because "you were not allowed" and "you got it wrong" are different security signals — a cluster of the former is what a SIEM alerts on. `error` marks a failure attributable to the server, so an operator can tell "users are getting it wrong" from "we are broken". The server enforces the pairing: `error` always carries the reason `internal_error` or `unavailable`, and those two reasons never appear under any other outcome. One of: `"success"`, `"failure"`, `"denied"`, `"error"`.
  - `severity` (enum, required): How much an event matters, lowest first. Assigned per action, and an occurrence may raise it but never lower it. A refusal or a server fault is raised to at least `warning` whatever its action's default, so `severity >= warning` selects everything that did not go as asked; a protocol step that only looks like a failure (`authorization_pending`, `use_dpop_nonce`, `mfa_required`, `passkey_offered`, `login_required`, `interaction_required`, `consent_required`, `account_selection_required`) keeps its action's default. One of: `"info"`, `"notice"`, `"warning"`, `"critical"`.
  - `organization_id` (string, required): The organization the event is ABOUT. Empty for a platform-scoped record (process startup, retention) — those have no owning tenant and are visible only through the main organization.
  - `cross_tenant` (boolean, required): Whether the actor acted from a different organization than the one the event is about — a main-org administrator operating on a tenant. Derived from the two organization ids.
  - `actor` (object, required): Who caused the event.

    `display` is a snapshot taken when the event was recorded, not a live
    lookup. That is deliberate: an audit record's most important reads happen
    after the fact, by which time the principal it names may have been
    deleted — and a log that can only render an opaque id once the row is
    gone has failed at its only job.
    - `type` (enum, required): The kind of principal that caused an event. `anonymous` is the correct value for every pre-authentication event: a failed sign-in has no principal yet, and the address that was typed is in the event metadata instead — putting it in the actor id would mint a phantom identifier that joins against nothing. One of: `"user"`, `"admin"`, `"client"`, `"system"`, `"anonymous"`.
    - `id` (string, optional)
    - `display` (string, optional): A human label captured at event time.
    - `impersonator_id` (string, optional): The real principal, when this actor was being acted-as. Present only for actions taken inside an impersonated session.
    - `impersonator_display` (string, optional)
  - `reason` (enum, optional): A stable machine token explaining a non-success outcome. Absent on success. One of: `"invalid_credentials"`, `"account_locked"`, `"rate_limited"`, `"mfa_required"`, `"email_verification_required"`, `"invalid_code"`, `"factor_locked"`, `"missing_role"`, `"missing_scope"`, `"cross_org_denied"`, `"not_found"`, `"conflict"`, `"invalid_request"`, `"invalid_client"`, `"invalid_grant"`, `"invalid_redirect_uri"`, `"unauthenticated"`, `"registration_closed"`, `"invalid_target"`, `"invalid_dpop_proof"`, `"repeated_header"`, `"token_expired"`, `"missing_expiry"`, `"audience_mismatch"`, `"unsupported_critical"`, `"unregistered_client"`, `"redirect_uri_required"`, `"pushed_request_required"`, `"invalid_request_uri"`, `"request_uri_client_mismatch"`, `"invalid_request_object"`, `"unavailable"`, `"internal_error"`, `"access_denied"`, `"login_required"`, `"consent_required"`, `"interaction_required"`, `"unsupported_grant_type"`, `"invalid_scope"`, `"unauthorized_client"`, `"unsupported_response_type"`, `"account_selection_required"`, `"invalid_authorization_details"`, `"expired_token"`, `"request_not_supported"`, `"request_uri_not_supported"`, `"unsupported_token_type"`, `"invalid_token"`, `"use_dpop_nonce"`, `"authorization_pending"`, `"slow_down"`, `"invalid_client_metadata"`, `"invalid_software_statement"`, `"unapproved_software_statement"`, `"account_unavailable"`, `"insufficient_user_authentication"`, `"protected_account"`, `"passkey_offered"`, `"password_change_required"`.

    Translate it rather than displaying it: **the key is the token verbatim
    in the `auditReasons` namespace** — `auditReasons:invalid_credentials`. A
    separate namespace from actions, because these are flat tokens rather
    than dotted paths and would otherwise nest unpredictably.

    The OAuth-related values are spelled exactly as the corresponding RFC
    6749 §5.2 / OIDC Core §3.1.2.6 wire error, so the audit record and the
    error the client actually received are the same string.

    **Compatibility:** closed, with the same cost described on `AuditAction` —
    this vocabulary grows as the server implements more specifications
    (`invalid_target` arrived with RFC 8707, `invalid_dpop_proof` with RFC
    9449).
  - `actor_organization_id` (string, optional): The organization the ACTOR came from.
  - `target` (object, optional): The object the event acted upon. Absent for events that act on nothing in particular. `display` is a snapshot, for the same reason as on the actor.
    - `type` (enum, optional): The kind of object an event acted upon. `audit_log` is the log itself, for the events that record reading or exporting it. `consent` is one user's standing agreement with one client for one resource, named by the agreement's id, so every decision about it shares one target. One of: `"user"`, `"organization"`, `"client"`, `"session"`, `"token"`, `"factor"`, `"role"`, `"system"`, `"audit_log"`, `"consent"`.
    - `id` (string, optional)
    - `display` (string, optional)
  - `client_ip` (string, optional): The caller's address, resolved through the trusted-proxy policy.
  - `client_ip_country` (string, optional): The ISO 3166-1 alpha-2 code of the country `client_ip` is in, read when the record is returned. Present only when the server has an IP geolocation database (`geoip.database`) that knows the address. Constraints: pattern `^[A-Z]{2}$`. Example: `"CH"`.
  - `client_ip_source` (enum, optional): How a client address was determined. `socket` means it is the connecting peer's own address, from a peer that forwarded no client, so it came off the connection and the caller could not have chosen it; `forwarded` means it came from a header set by a peer inside the trusted-proxy list; `proxy` means the request came through a trusted proxy that forwarded but named no client to believe, so the address is the proxy's own. One of: `"socket"`, `"forwarded"`, `"proxy"`.
  - `transport` (enum, optional): Whether the client's own hop to the deployment was encrypted, and on whose word. `tls`: this listener completed a TLS handshake and no trusted proxy reported a scheme; `cleartext`: it did not, and no trusted proxy reported a scheme. `forwarded_tls` and `forwarded_cleartext` are a trusted proxy's report (X-Forwarded-Proto): every hop it recorded was https, or one was not. It is the same verdict that decides whether the response carries Strict-Transport-Security, and it is computed by the server, never copied from a header. Absent on records with no request behind them (startup, maintenance, the pipeline's own records) and on the summary of a coalesced refusal, whose requests may have arrived differently; the summary of a coalesced edge finding (`http.forwarding.ignored`, `http.cleartext_credential.received`) carries the transport its window is keyed on. One of: `"tls"`, `"cleartext"`, `"forwarded_tls"`, `"forwarded_cleartext"`.
  - `proxy_ip` (string, optional): The trusted proxy that vouched for a forwarded `client_ip`: the address of the peer that connected to this server, present when `client_ip_source` is `forwarded` (every other source already puts that peer in `client_ip`). It is also set on a coalesced summary of forwarded requests (`http.cleartext_credential.received` carrying a `repeat_count`, whose window is the proxy's rather than a client's), where `client_ip` and `client_ip_source` are absent because the clients varied. A forwarded address is only as true as the peer that wrote it, and this names the peer.
  - `proxy_ip_country` (string, optional): The ISO 3166-1 alpha-2 code of the country `proxy_ip` is in, as `client_ip_country` is read. Constraints: pattern `^[A-Z]{2}$`. Example: `"CH"`.
  - `user_agent` (string, optional): The raw User-Agent, stored unparsed.
  - `client_kind` (enum, optional): The kind of client the record's User-Agent claims sent the request: `browser`, a browser a person drives; `ai_agent`, an AI crawler, assistant or agent (a training crawler, a page fetched for an assistant's user, a coding agent signing in to a tool); `crawler`, a search, link-preview or SEO crawler; `automation`, a headless or test-driven browser; `library`, a program calling directly (an HTTP library, a command-line tool, a native app, a health probe); `unknown`, a header that is empty or names none of these. It is read from the stored `user_agent` each time the record is served, so every record carries the server's current reading. A User-Agent is whatever its sender writes, so this is a claim, never a verified identity. Absent on records no request produced (startup, maintenance, the pipeline's own records) and on the summary of a coalesced refusal. One of: `"browser"`, `"ai_agent"`, `"crawler"`, `"automation"`, `"library"`, `"unknown"`.
  - `client_agent` (string, optional): The product a client other than a browser names in its User-Agent, as its vendor names it (`ClaudeBot`, `Claude Code`, `curl`), from the list the server recognizes. Absent when the header names none of them; never text copied from the header.
  - `request_id` (string, optional): Matches the `X-Request-ID` echoed to the caller. An inbound `X-Request-ID` is kept only from a trusted proxy; otherwise the server mints the id.
  - `trace_id` (string, optional): W3C trace id, for correlating with distributed traces. An inbound `traceparent` is honored only from a trusted proxy.
  - `span_id` (string, optional)
  - `operation` (string, optional): The OpenAPI operationId that served the request, exactly as this document spells it (`getJWKS`, `signIn`). A route mounted outside the document carries the name it declares: the per-organization discovery and key-set routes the operationId of the document they serve, the others a stable name of their own (`authUI`, `dashboard`, `healthz`, `pprof`). A control-plane event carries the gRPC full method name (`/package.Service/Method`). Empty when the request was refused before it was routed. Records stored by an earlier release carry the generated Go identifier instead (`GetJWKS`), so a query spanning the change matches both spellings. Stored instead of a request path, which would be unbounded in cardinality and would embed user ids and email addresses.
  - `http_method` (string, optional): The request method. A method outside the registered set (RFC 9110 §9 and PATCH) is recorded as `_OTHER`, as OpenTelemetry records it.
  - `session_id` (string, optional): The public id of the login session the event belongs to, the `sid` relying parties receive. An event an administrator caused through the admin API carries the session their access token was issued in.
  - `client_id` (string, optional): The OAuth client the server resolved the event to: the client that authenticated, the registered client a client authentication named (whether or not it succeeded), or the client of a verified token, session or flow. It says which registered client the event concerns, not that the request proved it was that client. An event an administrator caused names the admin client their access token was minted for (the console or the machine client), never a client they manage. Any other administrative or client lifecycle event never sets it; it names the client as its target.
  - `claimed_client_id` (string, optional): An OAuth client the event names that the server did not resolve it to: the `client_id` a sign-in form carried, an identifier a client authentication presented that no client registered, a logout's unverified hint. Present only when `client_id` is absent, so a record names its client in at most one of the two, and which one says whether the server resolved the client or the caller only named it. A named value is whatever the caller sent, bounded. Records written before the server kept this field carry it in `metadata.client_id` only.
  - `metadata` (object, optional): Per-action detail. Its keys are declared per action in the server's catalog (see the catalog endpoint) and anything undeclared is dropped before storage, so it can never carry credentials. A field with a closed vocabulary (the catalog's `field_values`) only ever holds a value from it. Two reserved keys may appear: `_dropped` lists keys that were removed — undeclared, or carrying a value outside their vocabulary — and `_truncated` lists keys whose values were shortened to fit the size bounds, so each such stored value is a prefix of the one the server was given. A key is never removed for its size.
- `page` (object, required): Where one page sits in a cursor (keyset) paginated list, returned beside that page's `items`.

  Walk a list by following `next_cursor` for as long as `has_more` is true,
  passing the token back verbatim as the `cursor` query parameter. Keep the
  listing and its filters the same for the whole walk; `limit` MAY change
  between pages (a cursor marks a position, not a page number), and `sort`
  travels in the cursor so it need not be resent. `has_more` is
  authoritative: it is derived from whether a further row actually exists,
  so a page that happens to be full is not mistaken for a page that has a
  successor, and a walk never costs a trailing empty request. `prev_cursor`
  steps back the same way.

  A cursor is opaque — server-defined, bound to this listing and sort
  order, and not parseable by clients. It stays valid across page-size
  changes but not across a change of sort order or filters; a cursor that
  does not belong to the request is rejected with a 400 rather than
  silently paging through the wrong set.

  Keyset paging is stable under concurrent writes, with one qualification.
  A row whose SORT KEY does not change is returned exactly once for the
  whole walk. Rows created or deleted mid-walk may or may not appear,
  depending on which side of the cursor they fall — and so may a row whose
  sort key is UPDATED mid-walk, since that moves it across the boundary: it
  can be seen twice or missed. Sorting by a field that does not change (the
  default id ordering, or `created_at`) avoids this entirely.
  - `limit` (integer, required): The page size in effect: the `limit` that was sent, or the server's default when it was omitted. Echoed so a client resuming from a stored cursor knows the page size without having to remember it. Example: `50`.
  - `sort` (string, required): The ordering this walk is pinned to, in the `sort` parameter's syntax and INCLUDING the primary-key term the server appends to make it total. Cursors carry it, so it need only be sent on the first request. Example: `"last_name,-first_name,id"`.
  - `has_more` (boolean, required): Whether a page follows this one and `next_cursor` can reach it. When false this is the last page and `next_cursor` is absent.
  - `has_prev` (boolean, required): Whether a page precedes this one and `prev_cursor` can reach it. False on the first page of a walk.

    Both flags report REACHABILITY, not bare existence. A cursor that overshoots the set — a stale bookmark, or rows deleted mid-walk — returns an empty page with both flags false even though rows precede it, because an empty page has no row to anchor a cursor on. Restart the walk if you land there; a client following `has_more` never does.
  - `next_cursor` (string, optional): Opaque cursor for the next page. Present exactly when `has_more` is true, and ABSENT (the field is omitted, not null) otherwise. Pass it back verbatim as the `cursor` query parameter; never parse it. Constraints: at most 512 characters.
  - `prev_cursor` (string, optional): Opaque cursor for the previous page. Present exactly when `has_prev` is true, and absent otherwise. Passed back as `cursor`, exactly like `next_cursor` — the token itself records which way it steps. Constraints: at most 512 characters.

### 400 Bad Request

The query was invalid — a time window that ends before it starts or spans more than 90 days, a value outside a filter's vocabulary, a malformed action pattern, a `client_ip` that is not an address, a `client_net` that is not an IPv4 /24 or IPv6 /64, a `network_zone` that is not a zone's name, or a pagination parameter that is malformed, foreign, or contradicts `sort`.

Content type: `application/problem+json`

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### 401 Unauthorized

Problem details (RFC 9457) with an RFC 6750 / RFC 9449 challenge.

Content type: `application/problem+json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### 403 Forbidden

Problem details (RFC 9457) with an RFC 6750 / RFC 9449 challenge.

Content type: `application/problem+json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### 429 Too Many Requests

Too many requests.

Content type: `application/problem+json`

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### Error

Problem details (RFC 9457) with an RFC 6750 / RFC 9449 challenge.

Content type: `application/problem+json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

## Example request

```bash
curl -g 'https://auth.example.com/v1/admin/orgs/{slug}/audit-events' \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

## Example response (200)

```json
{
  "items": [
    {
      "id": "string",
      "occurred_at": "2026-01-15T09:30:00Z",
      "recorded_at": "2026-01-15T09:30:00Z",
      "action": "admin.access.denied",
      "category": "authentication",
      "outcome": "success",
      "severity": "info",
      "organization_id": "string",
      "cross_tenant": false,
      "actor": {
        "type": "user"
      },
      "client_ip_country": "CH",
      "proxy_ip_country": "CH"
    }
  ],
  "page": {
    "limit": 50,
    "sort": "last_name,-first_name,id",
    "has_more": false,
    "has_prev": false
  }
}
```
