# Get the audit event catalog (admin)

`GET /v1/admin/audit-catalog`

Returns every audit event type this server can emit, with its category,
default severity, verbosity level, whether configuration can suppress it,
its declared metadata keys, and its translation key.

The vocabularies themselves are enumerated in this document, so filter
controls and translation coverage can be built statically without calling
this endpoint. What it adds is the per-action detail an enum cannot carry,
and the set THIS BUILD actually emits — which is what a client should check
against when the deployment it is talking to may be older or newer than the
contract it was generated from.

The catalog is a compile-time constant of the server, so its CONTENT is
identical for every organization and a client may hold it for the lifetime
of a build. The response itself is `no-store` and varies on the credential
all the same: what a caller receives here is 200-or-401 depending on the
token presented, so a shared cache that stored the 200 would hand the
catalog to a caller who presented nothing.

## 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.

## 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

The audit event catalog.

Content type: `application/json`

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

The complete audit event vocabulary this server can emit, plus the closed
value sets a client needs to render and filter it.

Every vocabulary here is also enumerated in this document, so a client
can build its filter controls and its translation coverage statically.
What this endpoint adds is the per-action DETAIL a bare enum cannot carry
— category, severity, verbosity level, whether configuration can suppress
it, its declared metadata keys, and its translation key — and the answer
to which values THIS BUILD actually emits, which may be a subset of what
the document declares once a deployment is older or newer than its
client.

Each array's element type matches the corresponding field on a record, so
a value that could be served is a value the document declares. A server
that could publish an action it cannot emit would be describing something
other than itself.

`field_values` covers the metadata fields whose values are a closed
vocabulary rather than data — the refusal cause, what triggered a
revocation, a stop or a degraded audit pipeline, a throttle's scope, the
step of a logout fanout, of a user_code submission or of a boot that
failed, the screen a client name was shown on, a client authentication
method, the algorithm a refused client assertion or request object
declared, what a consent decision did to the standing agreement, how a
UserInfo read resolved its subject, the client metadata field a
registration refusal or an unframeable front-channel logout was about,
what an RFC 7009 revocation request found, why a logout did not honor its
post-logout destination, what a logout's id_token_hint proved, what an
accepted authorization request was answered with, what Sec-Fetch-Site
said when a cross-site check refused on it, which profiling endpoint was
read, which dependency failed, which kind of operation and why, and what
the server uses it for, which forwarding headers an untrusted peer sent,
and where a request carried a credential over a hop a trusted proxy
reported as cleartext, how the server's database role could change or
remove audit records, and which writer lane signed a checkpoint of the
audit chain and which part of its statement a record holds — so a
detection rule can be written against the values the server will
actually use rather than the ones someone happened to see.

- `actions` (array of objects, required): One entry of the server's audit event catalog: everything about an action that does not vary per occurrence.
  - `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"`.
  - `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"`.
  - `level` (enum, required): The configured verbosity tier at which an action is recorded, least verbose first. Independent of severity: token refresh is low severity but the highest-volume event an identity provider produces, so it sits at the most verbose tier. It is the level of an action's successes: a refusal or a server fault is recorded at `security` whatever its action declares, so the default configuration keeps every refused token request and no issued one. The configuration also accepts `off`, which is not listed here because no catalog entry can declare it — an action recorded only when mandatory is a mandatory flag written confusingly. One of: `"security"`, `"write"`, `"read"`, `"trace"`.
  - `mandatory` (boolean, required): Whether configuration is unable to suppress this action. The non-suppressible set is small and deliberate: an operator who could switch off logging of destructive administrative actions would have a privilege-escalation path rather than a feature.
  - `platform` (boolean, required): Whether the action is platform-scoped (never owned by a tenant), such as process startup or audit retention.
  - `fields` (array of strings, required): The metadata keys an occurrence of this action may carry. It is a closed allowlist enforced at write time, which is what makes it impossible for a credential to reach the payload.
  - `i18n_key` (string, required): The translation key for this action — the action verbatim in the `auditEvents` namespace. Published here so a client never re-implements the derivation rule.
  - `summary` (string, required): A short English description. It is documentation and a last-resort fallback for a client with no translation yet — never the stored record.
- `categories` (array of enums, 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"`. Constraints: no duplicates.
- `outcomes` (array of enums, 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"`. Constraints: no duplicates.
- `severities` (array of enums, 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"`. Constraints: no duplicates.
- `reasons` (array of enums, required): The failure vocabulary this build can emit. A client translates a record's `reason` rather than displaying it, so it needs the full set to know which keys to provide. 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: no duplicates.

  Each item: A stable machine token explaining a non-success outcome. Absent on success. 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).
- `causes` (array of enums, required): The refusal causes this build can record in `metadata.cause`. One of: `"parameter_missing"`, `"grant_not_allowed"`, `"confidential_client_required"`, `"scope_not_registered"`, `"scope_exceeds_grant"`, `"resource_malformed"`, `"resource_not_granted"`, `"grant_client_mismatch"`, `"dpop_binding_mismatch"`, `"code_unknown"`, `"code_reused"`, `"redirect_uri_mismatch"`, `"pkce_verifier_missing"`, `"pkce_mismatch"`, `"pkce_required"`, `"pkce_verifier_unexpected"`, `"refresh_unknown"`, `"refresh_reused"`, `"session_ended"`, `"subject_revoked"`, `"grant_revoked"`, `"device_code_unknown"`, `"device_code_expired"`, `"authorization_denied"`, `"polling_too_fast"`, `"token_invalid"`, `"token_expired"`, `"missing_expiry"`, `"org_claim_mismatch"`, `"basic_header_malformed"`, `"multiple_auth_methods"`, `"client_id_mismatch"`, `"assertion_type_unsupported"`, `"client_unknown"`, `"client_lookup_failed"`, `"auth_method_mismatch"`, `"auth_method_unsupported"`, `"secret_missing"`, `"secret_not_registered"`, `"secret_mismatch"`, `"assertion_too_large"`, `"assertion_malformed"`, `"jwks_uri_missing"`, `"registered_alg_not_allowed"`, `"assertion_alg_not_allowed"`, `"assertion_typ_rejected"`, `"critical_header_unsupported"`, `"jwks_uri_invalid"`, `"jwks_fetch_failed"`, `"jwks_malformed"`, `"jwks_private_key_published"`, `"jwks_no_usable_key"`, `"assertion_kid_unknown"`, `"assertion_signature_invalid"`, `"assertion_sub_missing"`, `"assertion_sub_mismatch"`, `"assertion_iss_missing"`, `"assertion_iss_mismatch"`, `"assertion_aud_mismatch"`, `"assertion_exp_missing"`, `"assertion_expired"`, `"assertion_lifetime_too_long"`, `"assertion_not_yet_valid"`, `"assertion_iat_in_future"`, `"assertion_jti_missing"`, `"assertion_jti_too_large"`, `"assertion_is_request_object"`, `"assertion_replayed"`, `"replay_store_unavailable"`, `"request_canceled"`, `"panic_recovered"`, `"unknown_identifier"`, `"reset_link_unknown"`, `"verification_link_unknown"`, `"already_verified"`, `"invitation_link_unknown"`, `"password_mismatch"`, `"password_hash_malformed"`, `"password_not_set"`, `"password_held_by_directory"`, `"account_locked"`, `"identifier_locked"`, `"address_throttled"`, `"identifier_throttled"`, `"verify_capacity"`, `"request_abandoned"`, `"account_unavailable"`, `"cross_site_request"`, `"identifier_taken"`, `"reserved_identifier"`, `"password_already_set"`, `"mail_relay_missing"`, `"password_length"`, `"password_compromised"`, `"password_unscreened"`, `"captcha_missing"`, `"captcha_invalid"`, `"captcha_expired"`, `"captcha_replayed"`, `"captcha_unverifiable"`, `"code_mismatch"`, `"code_replayed"`, `"code_malformed"`, `"code_other_method"`, `"clock_skew"`, `"no_codes_left"`, `"lock_standing"`, `"no_factor"`, `"already_enrolled"`, `"enrollment_not_activated"`, `"method_not_permitted"`, `"passkey_taken"`, `"address_unverified"`, `"verifier_saturated"`, `"seed_key_unknown"`, `"seed_integrity_failed"`, `"webauthn_malformed"`, `"webauthn_wrong_type"`, `"webauthn_challenge_mismatch"`, `"webauthn_origin_unknown"`, `"webauthn_rpid_mismatch"`, `"webauthn_user_not_present"`, `"webauthn_user_not_verified"`, `"webauthn_backup_inconsistent"`, `"webauthn_backup_eligibility_changed"`, `"webauthn_algorithm_not_offered"`, `"webauthn_signature_invalid"`, `"webauthn_counter_regressed"`, `"webauthn_nothing_attested"`, `"webauthn_challenge_unknown"`, `"webauthn_key_not_held"`, `"webauthn_user_handle_mismatch"`, `"passwordless_closed"`, `"passkey_offer_absent"`, `"pending_absent"`, `"pending_unknown"`, `"pending_foreign_org"`, `"pending_expired"`, `"pending_not_enrollment"`, `"session_absent"`, `"session_unknown"`, `"session_foreign_org"`, `"session_expired"`, `"session_account_deleted"`, `"session_account_unavailable"`, `"issuer_unresolved"`, `"client_not_admin"`, `"org_claim_missing"`, `"grant_family_missing"`, `"software_statement_unsupported"`, `"blank_entry"`, `"value_unsupported"`, `"value_not_permitted"`, `"scope_reserved"`, `"refresh_without_issuing_grant"`, `"grant_response_mismatch"`, `"signing_alg_method_mismatch"`, `"too_many_values"`, `"uri_invalid"`, `"host_not_global"`, `"native_redirect_not_loopback"`, `"implicit_redirect_loopback"`, `"credential_missing"`, `"registration_token_invalid"`, `"operator_managed_client"`, `"client_secret_mismatch"`, `"concurrent_update"`, `"insufficient_scope"`, `"organization_missing"`, `"identity_not_allowed"`, `"operation_not_granted"`, `"organization_not_granted"`, `"namespace_not_granted"`, `"reserved_manager"`, `"admin_roles_label"`, `"invalid_field"`, `"immutable_field"`, `"main_organization"`, `"fips_unavailable"`, `"reserved_client_held"`, `"rp_timeout"`, `"rp_unreachable"`, `"rp_tls_failed"`, `"rp_transport_failed"`, `"rp_rejected"`, `"rp_misconfigured"`, `"rp_unavailable"`, `"logout_uri_invalid"`, `"no_delivery_slot"`, `"fanout_deadline"`, `"notifier_saturated"`, `"notifier_closed"`, `"egress_proxy_failed"`, `"aborted_at_shutdown"`, `"backchannel_not_registered"`, `"hint_absent"`, `"hint_malformed"`, `"hint_type_rejected"`, `"hint_key_unknown"`, `"hint_signature_invalid"`, `"hint_issuer_mismatch"`, `"hint_expired"`, `"hint_not_yet_valid"`, `"hint_claims_missing"`, `"hint_unverifiable"`, `"hint_subject_mismatch"`, `"logout_request_unknown"`, `"sid_missing"`, `"signing_key_unknown"`, `"signing_key_integrity_failed"`, `"token_malformed"`, `"token_typ_rejected"`, `"token_kid_unknown"`, `"token_alg_mismatch"`, `"token_signature_invalid"`, `"token_issuer_mismatch"`, `"token_issuer_unknown"`, `"token_not_yet_valid"`, `"dpop_proof_missing"`, `"dpop_proof_repeated"`, `"dpop_scheme_required"`, `"dpop_proof_too_large"`, `"dpop_malformed"`, `"dpop_typ_rejected"`, `"dpop_alg_not_allowed"`, `"dpop_jwk_missing"`, `"dpop_jwk_private"`, `"dpop_jwk_invalid"`, `"dpop_key_unsupported"`, `"dpop_signature_invalid"`, `"dpop_claim_missing"`, `"dpop_jti_too_large"`, `"dpop_htm_mismatch"`, `"dpop_htu_invalid"`, `"dpop_htu_mismatch"`, `"dpop_iat_too_old"`, `"dpop_iat_in_future"`, `"dpop_nonce_missing"`, `"dpop_nonce_stale"`, `"dpop_ath_missing"`, `"dpop_ath_mismatch"`, `"dpop_proof_replayed"`, `"signed_request_required"`, `"response_type_unsupported"`, `"response_mode_unsupported"`, `"dpop_jkt_without_code"`, `"claims_malformed"`, `"pkce_method_unsupported"`, `"account_selection_unsupported"`, `"authorization_details_malformed"`, `"authorization_details_type_not_registered"`, `"request_object_malformed"`, `"request_object_alg_not_allowed"`, `"request_object_kid_unknown"`, `"request_object_signature_invalid"`, `"request_object_aud_mismatch"`, `"request_object_iss_mismatch"`, `"request_object_expired"`, `"request_object_not_yet_valid"`, `"request_uri_in_push"`, `"request_and_request_uri"`, `"request_uri_not_pushed"`, `"request_uri_not_registered"`, `"request_uri_fetch_failed"`, `"request_uri_consumed"`, `"max_age_exceeded"`, `"consent_not_enabled"`, `"consent_not_granted"`, `"body_too_large"`, `"body_malformed"`, `"parameter_repeated"`, `"schema_invalid"`, `"cors_origin_denied"`, `"throttle_failed_open"`, `"throttle_failed_closed"`, `"cursor_tampered"`, `"cursor_scope_mismatch"`, `"cursor_sort_mismatch"`, `"unknown_organization"`, `"main_organization_prefixed"`, `"organization_lookup_failed"`, `"organization_build_failed"`, `"issuer_location_mismatch"`, `"sign_in_unknown"`, `"sign_in_too_old"`, `"second_factor_required"`, `"own_account"`, `"target_outranks_caller"`, `"owner_tier_required"`, `"account_linked"`, `"platform_role_outside_main"`, `"role_outranks_caller"`, `"last_role_administrator"`, `"federation_state_invalid"`, `"upstream_denied"`, `"upstream_unavailable"`, `"upstream_token_invalid"`, `"upstream_email_missing"`, `"identity_provider_unknown"`, `"identity_provider_not_allowed"`, `"account_not_linked"`, `"email_domain_not_allowed"`, `"membership_exists"`, `"membership_nested"`, `"membership_self"`, `"membership_wrong_side"`, `"membership_inactive"`, `"member_issuer_mismatch"`, `"member_sign_in_stale"`, `"domain_claimed"`, `"domain_record_missing"`, `"domain_owned"`, `"domain_lookup_failed"`, `"directory_entry_ambiguous"`, `"directory_entry_unlinkable"`, `"sign_up_closed"`, `"directory_account_disabled"`, `"directory_account_locked"`, `"directory_account_expired"`, `"directory_password_expired"`, `"directory_password_change_required"`, `"directory_account_restricted"`, `"operator_managed"`. Constraints: no duplicates.

  Each item: The RULE that produced a refusal, carried in a record's `metadata.cause`. A reason is the error the client received, and one error covers many rules on purpose: `invalid_grant` answers an unknown code, a PKCE mismatch and a refresh token presented by the wrong client alike, because telling the caller which one fired would tell them whether the credential they hold is genuine. The cause is the operator's half of that distinction. Each token names one rule and means the same thing at every endpoint that applies it; the action, `grant_type`, `method` and `metadata_field` say where. The second factor uses it the same way: one 401 answers a wrong code, a replayed one, a malformed one and a genuine one from a device whose clock is off, and refuses a missing, unknown, expired or foreign pending sign-in or session alike. The cause is where they differ. A cause names a rule, never a value: no code, verifier, redirect URI or error description ever appears here. A value outside this vocabulary is never stored — the server drops it and lists `cause` under `_dropped`. **Compatibility:** closed and append-only, with the cost described on `AuditAction`.
- `exemptions` (array of enums, required): Why this build can spare a sign-in its second factor, as recorded in `metadata.mfa_spared`. One of: `"remembered_browser"`, `"network_zone"`, `"identity_provider"`. Constraints: no duplicates.

  Each item: Why a completed sign-in by a user who holds a second factor was not asked for it, carried in an `auth.sign_in.completed` record's `metadata.mfa_spared`. `remembered_browser`: the browser passed a second factor for the user within the policy's window; `network_zone`: the sign-in came from a network zone the policy spares in; `identity_provider`: the sign-in came through an identity provider that spares the second factor, so it was neither challenged nor sent to enroll one. Absent when nothing spared it.
- `actor_types` (array of enums, 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"`. Constraints: no duplicates.
- `target_types` (array of enums, required): 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"`. Constraints: no duplicates.
- `field_values` (array of objects, required): The closed value set of every metadata field that has one, in the server's field order.

  Each item: The closed set of values one metadata field may carry. A value outside it is never stored: the server drops it and lists the key under `_dropped`, exactly as it does an undeclared key. A field that holds a list (`fails_closed`, `fails_open`, `carried_in`, `headers`, `audit_rewrite_routes`) holds only values from the set, and is dropped whole if one is not.
  - `field` (string, required): The metadata key.
  - `values` (array of strings, required): Every value the field may carry, in the server's declared order.

### 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 'https://auth.example.com/v1/admin/audit-catalog' \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

## Example response (200)

```json
{
  "actions": [
    {
      "action": "admin.access.denied",
      "category": "authentication",
      "severity": "info",
      "level": "security",
      "mandatory": false,
      "platform": false,
      "fields": [
        "string"
      ],
      "i18n_key": "string",
      "summary": "string"
    }
  ],
  "categories": [
    "authentication"
  ],
  "outcomes": [
    "success"
  ],
  "severities": [
    "info"
  ],
  "reasons": [
    "invalid_credentials"
  ],
  "causes": [
    "parameter_missing"
  ],
  "exemptions": [
    "remembered_browser"
  ],
  "actor_types": [
    "user"
  ],
  "target_types": [
    "user"
  ],
  "field_values": [
    {
      "field": "string",
      "values": [
        "string"
      ]
    }
  ]
}
```
