Skip to content

Events

An event records something that happened in QAuth: a sign-in, a refused token request, a new client. Events exist so that automation outside QAuth can act on them (ADR-018 §3). ADR-018 names four routes for them: signed webhooks in the Standard Webhooks format, a cursor-based event API, Shared Signals Framework streams, and OpenTelemetry export. Every route carries the same event, in the envelope below. The event types and their payload schemas are part of the 1.0 stability contract (ADR-018 §5).

This page is generated from the type registry in libs/shared/events, together with the JSON Schema files it links.

Every event has these members. type, category and data depend on the type; the others mean the same for every type. A member that does not apply is absent, never null.

{
"id": "0199f2b4-7c3e-7a51-9d2b-3a6c8e1f0a42",
"type": "urn:qauth:event:user.signed_in:v1-rc.0",
"time": "2026-10-22T09:30:00.123Z",
"realm": "0199f2b4-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"category": "user",
"actor": {
"type": "user",
"id": "0199f2b4-2b3c-7d4e-9f5a-6b7c8d9e0f1a"
},
"subject": {
"type": "user",
"id": "0199f2b4-2b3c-7d4e-9f5a-6b7c8d9e0f1a"
},
"client": {
"id": "notes-app"
},
"request": {
"traceId": "req-7f3a",
"ip": "203.0.113.7",
"userAgent": "Mozilla/5.0"
},
"data": {
"method": "password"
}
}
MemberTypeRequiredMeaning
idUUIDyesThe event id, a UUIDv7. One event keeps one id on every route and every retry, so a receiver drops duplicates by it. It is the webhook-id of a webhook and the source of an SSF jti.
typestringyesThe event type, urn:qauth:event:<name>:v<N>, or :v<N>-rc.<M> for a release candidate. The registry lists every type.
timetime (RFC 3339, UTC)yesWhen the event happened, as an RFC 3339 time in UTC. Not when it was delivered.
realmUUIDyesThe id of the realm the event happened in.
categoryone of user, adminyesuser: an end user’s own activity. admin: an administrator’s change. Fixed by the type.
actorobjectnoWho or what caused the event.
actor.typeone of user, client, system, operator_cli, or a later valueyesuser: a person, signed in. client: an OAuth client acting for itself or, with delegationChain, as an agent for the subject. system: QAuth itself, such as a scheduled job. operator_cli: an operator on the server host, through a QAuth command-line tool.
actor.idstringnoThe actor’s identifier: a user id, a client’s client_id, or the operating-system user for operator_cli. Absent for system.
actor.delegationChainarray of stringnoThe RFC 8693 act chain of a delegated request, as client_id values: the acting agent first, then each earlier actor. Absent when nothing was delegated.
subjectobjectnoWhat the event is about.
subject.typeone of user, client, api_key, ssf_stream, or a later valueyesWhat kind of thing the subject is.
subject.idstringyesThe subject’s identifier: a user id, a client’s client_id, an API key id or a Shared Signals Framework stream_id. It stays valid after the subject is deleted.
clientobjectnoThe OAuth client the request came through.
client.idstringyesThe client’s client_id.
requestobjectnoThe HTTP request that caused the event.
request.traceIdstringnoThe id under which QAuth logged the request: the trace_id of a problem document from the same request.
request.ipIPv4 address or IPv6 addressnoThe end user’s IP address, as QAuth derived it from the request. An IPv4-mapped IPv6 address is written in its IPv4 form.
request.userAgentstringnoThe User-Agent header, cut to 1024 characters.
dataobjectyesThe type’s own payload. Its schema is the type’s.
  • The id is a UUIDv7, assigned once when the event is recorded. A retry, a redelivery and every route repeat it, so a receiver drops duplicates by it. A webhook’s webhook-id header is the event id, and an SSF Security Event Token’s jti is derived from it.
  • The subject is a plain id with no database link: it stays valid after the user or the client is deleted.
  • A webhook body is the envelope. Standard Webhooks recommends a full-stop delimited type, a timestamp and data: the short name in the middle of the identifier is the delimited type, and time is the timestamp.
  • The envelope’s schema is envelope.json.

These rules hold for every event’s data. They were written for the audit log (ADR-011 §8, ADR-017 §11) and apply to events.

  1. Ids, not names or addresses. data names a user, a client or a record by its id. A type that needs a name, an email address, a phone number or an IP address declares that field in its registry entry. No type registered today does.
  2. Never a secret. No password, code, OTP, token, key, assertion, signature, cookie, secret, or set-up or reset link, in any form. There is no exception. An admin event’s diff shows a changed resource’s own fields; there a secret field holds ***, never its value.
  3. Hidden identifiers are keyed HMACs. An identifier that must not be revealed, such as the address a failed sign-in typed, is carried as an HMAC under a server-side key, never as the value and never as a plain hash.

A scanner enforces the rules. The registry’s tests fail when a type’s schema declares a field named like a secret, or like personal data the type has not declared. parseEvent, the check a single event passes before it is stored or sent, applies them again: a field named like a secret, or a value shaped like a token or a key, refuses the event. The scanner reads names, so a field that holds an id, a type or a count of such a thing passes when its name ends in Id, Type or Count (apiKeyId, issuedTokenType).

An admin event records a change an administrator, a service account or an operator made (ADR-020 §3, ADR-019 Decision 7). Admin events are always on: no environment key and no realm setting turns them off. Each is written in the transaction of the change it records, so the change and its event commit together.

The diff. data.diff lists what the operation changed, one entry per field:

  • path names the field, as a JSON Pointer (RFC 6901) into the changed resource, such as /smtp/host.
  • before and after hold its value. before is absent when the field did not exist, and after when it no longer does; null means the field held null.
  • Only changed fields are listed. A nested object present on both sides is diffed member by member. Any other value, an array included, is shown whole.
[
{ "path": "/displayName", "before": "Notes", "after": "Team notes" },
{ "path": "/smtp/password", "before": "***", "after": "***" },
{ "path": "/allowedScopes", "before": ["openid"], "after": ["openid", "profile"] }
]

Masking. A secret field holds *** on both sides, whatever its value. A field is secret when the operation marks it, or when its name holds a secret, marked or not: a password, pass, PIN, secret, token, key, salt, pepper, cookie, signature, credential, auth or connection string, a key-encryption or data-encryption key (kek, dek), a set-up, reset or other one-time link, or a one-time password’s link (totpUri). A name whose last word only describes the secret is shown: accessTokenTtl, tokenEndpointAuthMethod, signingKeyId, jwksUri, keyUse, saltRounds, authServer. So is a code or a key that only names something: countryCode, attrKey. A changed secret is still listed, so a rotation shows as *** to ***. A flag (true or false) is never masked.

A value shaped like a secret is masked wherever it sits: a token, a signed or encrypted object, a PEM block, an Authorization header value, a private or symmetric JWK, or a URL that carries a credential: one with a password (or, over HTTP, a user name), a query or fragment member named like a secret (?access_token=, ?sig=, ?key=, ?code=), an otpauth:// URI, or a webhook URL that holds its secret in the path. A public JWK is shown, and a list of keys shows its public keys with each private one masked. A member name shaped like a secret refuses the event.

The field names in a diff are the resource’s own, so the payload scanner reads a diff’s values, not its names, and checks instead that every field named like a secret holds ***. A diff may show personal data, such as a contact address an admin changed: admin events are read by the realm’s admins.

Approvals. data.approvals lists the approvals the operation ran on: both approvals of a two-admin operation, each with the approver, the passkey credential it was given with, and who created the approver’s account.

The hash chain. The event store chains a realm’s admin events: each stores the hash of the one before it, and its own hash is SHA-256 over the chain format’s id (qauth-event-chain/1-rc.0, a release candidate until 1.0.0), that hash, the id of the transaction that wrote the event, and the event’s canonical JSON (RFC 8785). A changed event breaks the chain at that event, and a removed one at the event after it. The chain’s hashes are stored beside the event and are not part of the envelope.

An event type’s identifier is urn:qauth:event:<name>:v<N>. The <name> is its short name: lower-case snake_case segments joined by full stops, the resource first. <N> is the major version of its data. Every QAuth-defined identifier carries a version (ADR-018 §5).

Release candidates. Until QAuth 1.0.0, every type is a release candidate and its identifier ends in -rc.<M>, as in urn:qauth:event:user.signed_in:v1-rc.0. Nothing about a candidate is promised. A breaking change before 1.0.0 raises <M> (:v1-rc.0 becomes :v1-rc.1), so an identifier never names two different schemas. The release that ships QAuth 1.0.0 drops the suffix from every type: :v1 is the version under the stability promise, and the rules below apply from then on.

Within a major release, the contract changes only by addition:

  • a new type, which is a new entry in the registry;
  • a new optional member of a type’s data, or of the envelope;
  • a new value of an open set (x-extensible-enum, shown on this page as “or a later value”).

Anything else is a breaking change: removing or renaming a member, changing its type, making a member required, adding a required member, or narrowing what a member accepts. A breaking change makes a new type with the next version, such as :v2. QAuth emits both versions for every event until the deprecation window ends, so a receiver moves at its own pace. The old type is marked deprecated and names its replacement. It is removed only in a new major release, no sooner than 12 months after the deprecation (ADR-018 §5).

A receiver ignores members it does not know, and accepts a value of an open set it does not know. The published schemas leave objects open for that reason. A closed set, such as category, does not grow within a major release.

The stable-surface check compares each schema file with the last release and fails on a breaking change that no one approved.

Each type has a JSON Schema (draft 2020-12) of its whole event: the envelope, with type, category and data fixed to the type. They are served under /schemas/events/, and each file’s $id is its URL. The envelope’s own schema accepts an event of any type.

TypeCategorySeveritySince
urn:qauth:event:user.signed_in:v1-rc.0userinfov0.2.0
urn:qauth:event:user.sign_in_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:user.signed_out:v1-rc.0userinfov0.2.0
urn:qauth:event:user.sign_out_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:user.registered:v1-rc.0userinfov0.2.0
urn:qauth:event:user.registration_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:authorization.completed:v1-rc.0userinfov0.2.0
urn:qauth:event:authorization.failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:step_up.required:v1-rc.0userinfov0.2.0
urn:qauth:event:step_up.elevated:v1-rc.0userinfov0.2.0
urn:qauth:event:consent.granted:v1-rc.0userinfov0.2.0
urn:qauth:event:consent.denied:v1-rc.0userinfov0.2.0
urn:qauth:event:consent.revoked:v1-rc.0userinfov0.2.0
urn:qauth:event:token.issued:v1-rc.0userinfov0.2.0
urn:qauth:event:token.request_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:token.revoked:v1-rc.0userinfov0.2.0
urn:qauth:event:token.revocation_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:token.introspected:v1-rc.0userinfov0.2.0
urn:qauth:event:token.introspection_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:userinfo.served:v1-rc.0userinfov0.2.0
urn:qauth:event:userinfo.request_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:client.created:v1-rc.0userinfov0.2.0
urn:qauth:event:client.updated:v1-rc.0userinfov0.2.0
urn:qauth:event:client.deleted:v1-rc.0userinfov0.2.0
urn:qauth:event:client.secret_rotated:v1-rc.0userinfov0.2.0
urn:qauth:event:client.suspended:v1-rc.0adminwarningv0.2.0
urn:qauth:event:client.suspension_lifted:v1-rc.0admininfov0.2.0
urn:qauth:event:api_key.created:v1-rc.0userinfov0.2.0
urn:qauth:event:api_key.revoked:v1-rc.0userinfov0.2.0
urn:qauth:event:wallet.linked:v1-rc.0userinfov0.2.0
urn:qauth:event:wallet.link_failed:v1-rc.0userwarningv0.2.0
urn:qauth:event:wallet.presentation_received:v1-rc.0userinfov0.2.0
urn:qauth:event:request.csrf_rejected:v1-rc.0userwarningv0.2.0
urn:qauth:event:ssf.stream.verification_requested:v1-rc.0userinfov0.2.0
urn:qauth:event:admin.change_applied:v1-rc.0admininfov0.2.0

A person signed in: the credential checked out and QAuth started a browser session or issued tokens.

  • Identifier: urn:qauth:event:user.signed_in:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: user.signed_in.v1-rc.0.json
MemberTypeRequiredMeaning
methodone of password, wallet, or a later valueyespassword: an identifier and a password. wallet: a credential presented from a wallet (OpenID4VP). The set may grow in a minor release.
verifierProfilestringnoThe verifier profile the wallet presentation was checked under, such as haip-1.0.

A sign-in was refused: an unknown identifier, a wrong credential, a locked or disabled account.

  • Identifier: urn:qauth:event:user.sign_in_failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: user, when known
  • Schema: user.sign_in_failed.v1-rc.0.json
MemberTypeRequiredMeaning
methodone of password, wallet, or a later valueyespassword: an identifier and a password. wallet: a credential presented from a wallet (OpenID4VP). The set may grow in a minor release.
reasonstringyesWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.
identifierHmacstringnoA keyed HMAC (HMAC-SHA-256, base64url) of the identifier the person typed, so repeated attempts at one identifier can be correlated without revealing it. Absent when the attempt named a known user, who is then the subject.
verifierProfilestringnoThe verifier profile the wallet presentation was checked under, such as haip-1.0.

A person signed out, and QAuth ended their sessions.

  • Identifier: urn:qauth:event:user.signed_out:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: user.signed_out.v1-rc.0.json

data has no members: the envelope carries the event.

A sign-out request was refused or could not complete.

  • Identifier: urn:qauth:event:user.sign_out_failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: user, when known
  • Schema: user.sign_out_failed.v1-rc.0.json
MemberTypeRequiredMeaning
reasonstringyesWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.

A person created an account.

  • Identifier: urn:qauth:event:user.registered:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: user.registered.v1-rc.0.json
MemberTypeRequiredMeaning
methodone of password, wallet, or a later valueyespassword: an identifier and a password. wallet: a credential presented from a wallet (OpenID4VP). The set may grow in a minor release.

An account could not be created, for example because the address is in use.

  • Identifier: urn:qauth:event:user.registration_failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: none: the event never carries one
  • Schema: user.registration_failed.v1-rc.0.json
MemberTypeRequiredMeaning
reasonstringyesWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.
identifierHmacstringnoA keyed HMAC (HMAC-SHA-256, base64url) of the identifier the person typed, so repeated attempts at one identifier can be correlated without revealing it. Absent when the attempt named a known user, who is then the subject.

The authorization endpoint sent the client back with an authorization response that grants access.

  • Identifier: urn:qauth:event:authorization.completed:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: authorization.completed.v1-rc.0.json

data has no members: the envelope carries the event.

The authorization endpoint refused a request: an unknown client, a redirect URI that is not registered, a scope the client may not have, or a denial.

  • Identifier: urn:qauth:event:authorization.failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: user, when known
  • Schema: authorization.failed.v1-rc.0.json
MemberTypeRequiredMeaning
errorstringyesThe OAuth or OpenID Connect error code the request was refused with, such as invalid_grant.
reasonstringnoWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.

A request needs a fresh sign-in or new consent before it can go on, for example for a dangerous scope (ADR-007 §2).

  • Identifier: urn:qauth:event:step_up.required:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: step_up.required.v1-rc.0.json
MemberTypeRequiredMeaning
reasonstringyesWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.
errorstringnoThe OAuth or OpenID Connect error code the request was refused with, such as invalid_grant.
elevatedScopesarray of stringnoThe scopes the request adds to what the user already granted.
dangerousScopesarray of stringnoThe requested scopes classed as dangerous.
promptstringnoThe OpenID Connect prompt value of the request.
maxAgeintegernoThe max_age of the request, in seconds.

After a fresh sign-in, a user granted a client dangerous scopes (ADR-007 §2).

  • Identifier: urn:qauth:event:step_up.elevated:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: step_up.elevated.v1-rc.0.json
MemberTypeRequiredMeaning
scopesarray of stringyesEvery scope granted.
dangerousScopesarray of stringyesThe granted scopes classed as dangerous.
scopeModeone of readonly, admin, exec, or a later valuenoThe agent scope mode (ADR-007 §2) the request was granted: readonly, admin or exec.
authenticatedAttime (RFC 3339, UTC)noWhen the fresh sign-in happened, as an RFC 3339 time in UTC.

A user granted a client the scopes it asked for.

  • Identifier: urn:qauth:event:consent.granted:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: consent.granted.v1-rc.0.json
MemberTypeRequiredMeaning
scopesarray of stringyesThe scopes granted.
rememberedbooleanyesWhether the user chose to remember the decision for later requests.
dangerousScopesarray of stringnoThe granted scopes classed as dangerous.

Consent was not given: the user declined, or the request did not match what the consent screen showed.

  • Identifier: urn:qauth:event:consent.denied:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: consent.denied.v1-rc.0.json
MemberTypeRequiredMeaning
errorstringnoThe OAuth or OpenID Connect error code the request was refused with, such as invalid_grant.
reasonstringnoWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.

A user withdrew a client’s consent. QAuth revoked the refresh tokens the consent covered.

  • Identifier: urn:qauth:event:consent.revoked:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: consent.revoked.v1-rc.0.json
MemberTypeRequiredMeaning
consentIdUUIDyesThe id of the consent that was withdrawn.
revokedRefreshCountintegernoHow many refresh tokens were revoked with it.

The token endpoint issued tokens: for an authorization code, a refresh, client credentials, a token exchange or a JWT bearer assertion.

  • Identifier: urn:qauth:event:token.issued:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: token.issued.v1-rc.0.json
MemberTypeRequiredMeaning
grantTypestringyesThe grant_type of the token request, such as authorization_code.
scopesarray of stringnoThe scopes of the issued access token.
audiencearray of stringnoThe audience of the issued token.
resourcesarray of stringnoThe RFC 8707 resource indicators the token is bound to.
expiresInintegernoThe lifetime of the issued access token, in seconds.
issuedTokenTypestringnoThe RFC 8693 issued_token_type of a token exchange.
refreshFamilyIdUUIDnoThe refresh family the issued refresh token belongs to. Every rotation keeps it.
delegationDepthintegernoHow many actors the RFC 8693 act chain of the issued token holds.
idJagIdstringnoThe jti of the identity assertion authorization grant minted or consumed (ADR-011). Never the grant itself.
idJagIssuerstringnoThe issuer of a consumed identity assertion authorization grant (ADR-011).
scopeModeone of readonly, admin, exec, or a later valuenoThe agent scope mode (ADR-007 §2) the request was granted: readonly, admin or exec.

The token endpoint refused a request: failed client authentication, an invalid or replayed grant, a scope or resource outside what was granted.

  • Identifier: urn:qauth:event:token.request_failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: user, when known
  • Schema: token.request_failed.v1-rc.0.json
MemberTypeRequiredMeaning
grantTypestringnoThe grant_type of the token request, such as authorization_code.
errorstringyesThe OAuth or OpenID Connect error code the request was refused with, such as invalid_grant.
reasonstringnoWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.
refreshFamilyIdUUIDnoThe refresh family a replayed grant (reason code_replay or refresh_replay) had issued, which the refusal revoked. Absent when the grant issued none.
revokedRefreshCountintegernoHow many refresh tokens of that family the refusal revoked; 0 when they were revoked already. Present for the reasons code_replay and refresh_replay.

A client revoked a token at the RFC 7009 revocation endpoint.

  • Identifier: urn:qauth:event:token.revoked:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: token.revoked.v1-rc.0.json
MemberTypeRequiredMeaning
typeHintstringnoThe token_type_hint the client sent, such as refresh_token.

The revocation endpoint refused a request, for example on failed client authentication.

  • Identifier: urn:qauth:event:token.revocation_failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: none: the event never carries one
  • Schema: token.revocation_failed.v1-rc.0.json
MemberTypeRequiredMeaning
errorstringyesThe OAuth or OpenID Connect error code the request was refused with, such as invalid_grant.
reasonstringnoWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.

A resource server introspected an active token (RFC 7662).

  • Identifier: urn:qauth:event:token.introspected:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: token.introspected.v1-rc.0.json
MemberTypeRequiredMeaning
tokenClientIdstringnoThe client_id the introspected token was issued to.

An introspection request failed: client authentication, or a token that is invalid, expired, revoked or not one the caller may inspect.

MemberTypeRequiredMeaning
errorstringyesThe OAuth or OpenID Connect error code the request was refused with, such as invalid_grant.
reasonstringnoWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.

The userinfo endpoint returned claims for an access token.

  • Identifier: urn:qauth:event:userinfo.served:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: userinfo.served.v1-rc.0.json

data has no members: the envelope carries the event.

The userinfo endpoint refused a request.

  • Identifier: urn:qauth:event:userinfo.request_failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: user, when known
  • Schema: userinfo.request_failed.v1-rc.0.json
MemberTypeRequiredMeaning
errorstringyesThe OAuth or OpenID Connect error code the request was refused with, such as invalid_grant.
reasonstringnoWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.

An OAuth client was created: by RFC 7591 dynamic registration, or by a developer through the developer API.

  • Identifier: urn:qauth:event:client.created:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: client, when known
  • Schema: client.created.v1-rc.0.json
MemberTypeRequiredMeaning
registrationone of dynamic, developer_api, or a later valueyesdynamic: RFC 7591 dynamic client registration. developer_api: a developer created it. The set may grow in a minor release.
isPublicbooleanyesWhether the client is public, with no client authentication.
isAgentbooleannoWhether the client registered as an agent (ADR-007 §2).
grantTypesarray of stringyesThe grant types the client may use.
scopesarray of stringnoThe scopes the client may request.
authMethodstringnoHow the client authenticates at the token endpoint, such as private_key_jwt.

A developer changed a client’s settings.

  • Identifier: urn:qauth:event:client.updated:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: client, when known
  • Schema: client.updated.v1-rc.0.json
MemberTypeRequiredMeaning
fieldsarray of stringyesThe names of the settings that changed. Never their values.

A developer deleted a client.

  • Identifier: urn:qauth:event:client.deleted:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: client, when known
  • Schema: client.deleted.v1-rc.0.json

data has no members: the envelope carries the event.

A developer replaced a client’s secret. The old secret stopped working.

  • Identifier: urn:qauth:event:client.secret_rotated:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: client, when known
  • Schema: client.secret_rotated.v1-rc.0.json

data has no members: the envelope carries the event.

An operator suspended a client: it can no longer obtain tokens until the suspension is lifted.

  • Identifier: urn:qauth:event:client.suspended:v1-rc.0
  • Category: admin · Severity: warning · Since: v0.2.0
  • Subject: client, when known
  • Schema: client.suspended.v1-rc.0.json

data has no members: the envelope carries the event.

An operator lifted a client’s suspension.

  • Identifier: urn:qauth:event:client.suspension_lifted:v1-rc.0
  • Category: admin · Severity: info · Since: v0.2.0
  • Subject: client, when known
  • Schema: client.suspension_lifted.v1-rc.0.json

data has no members: the envelope carries the event.

A developer created an API key for a client.

  • Identifier: urn:qauth:event:api_key.created:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: api_key, when known
  • Schema: api_key.created.v1-rc.0.json

data has no members: the envelope carries the event.

A developer revoked an API key. It stopped working.

  • Identifier: urn:qauth:event:api_key.revoked:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: api_key, when known
  • Schema: api_key.revoked.v1-rc.0.json

data has no members: the envelope carries the event.

A signed-in user linked a wallet credential to their account.

  • Identifier: urn:qauth:event:wallet.linked:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: user, when known
  • Schema: wallet.linked.v1-rc.0.json
MemberTypeRequiredMeaning
credentialIdstringyesThe id of the account credential the wallet was linked as.
subjectSourcestringyesHow the subject was resolved from the presentation (ADR-009).
reboundbooleanyesWhether the link replaced an earlier binding of the same credential.
verifierProfilestringyesThe verifier profile the wallet presentation was checked under, such as haip-1.0.

A wallet could not be linked, for example because it is linked to another account.

  • Identifier: urn:qauth:event:wallet.link_failed:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: user, when known
  • Schema: wallet.link_failed.v1-rc.0.json
MemberTypeRequiredMeaning
reasonstringyesWhy, as a snake_case code such as invalid_credentials. The set may grow in a minor release.
verifierProfilestringnoThe verifier profile the wallet presentation was checked under, such as haip-1.0.

The OpenID4VP response endpoint accepted a wallet presentation. This is not a sign-in: the flow that asked for it decides.

  • Identifier: urn:qauth:event:wallet.presentation_received:v1-rc.0
  • Category: user · Severity: info · Since: v0.2.0
  • Subject: none: the event never carries one
  • Schema: wallet.presentation_received.v1-rc.0.json
MemberTypeRequiredMeaning
verifierProfilestringyesThe verifier profile the wallet presentation was checked under, such as haip-1.0.
presentationCountintegeryesHow many presentations the response held.

A form post was refused because its CSRF check failed: the token was missing or did not match.

  • Identifier: urn:qauth:event:request.csrf_rejected:v1-rc.0
  • Category: user · Severity: warning · Since: v0.2.0
  • Subject: user, when known
  • Schema: request.csrf_rejected.v1-rc.0.json
MemberTypeRequiredMeaning
formone of sign_in, wallet_sign_in, wallet_link, consent, consent_revocation, or a later valueyesThe form that was posted. The set may grow in a minor release.

A receiver asked for a verification event on one of its Shared Signals Framework streams (SSF 1.0 §8.1.4). QAuth sends the verification Security Event Token to that stream only.

MemberTypeRequiredMeaning
statestringnoThe opaque value the receiver sent, returned to it in the verification event. Absent when it sent none.

An administrator, a service account or an operator changed something in the realm through an admin operation. Emitted for every such change, in its transaction; no setting turns it off. The admin events of a realm form one hash chain.

  • Identifier: urn:qauth:event:admin.change_applied:v1-rc.0
  • Category: admin · Severity: info · Since: v0.2.0
  • Subject: none: the event never carries one
  • Schema: admin.change_applied.v1-rc.0.json
  • Diff: data.diff lists what changed, with every secret value masked (Admin events)
MemberTypeRequiredMeaning
operationstringyesThe operation’s id in the permission catalog, such as realms.policies.write (ADR-019 Decision 7).
targetobjectyesWhat the operation changed.
target.typestringyesWhat kind of thing the operation changed, as a snake_case code such as realm, client or webhook_subscription. The set grows with the admin API.
target.idstringyesThe changed thing’s identifier, such as a realm id or a client_id. It stays valid after the thing is deleted.
diffarray of objectyesWhat the operation changed in the target, field by field. Only changed fields are listed. A nested object is diffed member by member; any other value, an array included, is shown whole. Every secret value is ***.
approvalsarray of objectyesThe approvals the operation ran on, both of a two-admin operation. Empty when it needed none.

Before the event contract, QAuth wrote free-form event names to its audit log and its log lines. Each becomes a registered type. Several earlier names become one type, and the name implied some data. Since 0.2.0 the auth server records events and writes no audit row; the upgrade copies every audit row into the event store with this map. A copied event gets the data its name implies and the members of the old row that have the declared form. A required member an old row cannot supply gets a fixed value: unknown for a code, false for a flag, an empty list, 0 for a count or the nil UUID for an id. Log lines keep their names.

Earlier nameWritten toTypeImplied data
user.login.successaudit logurn:qauth:event:user.signed_in:v1-rc.0method: password
user.login.failureaudit logurn:qauth:event:user.sign_in_failed:v1-rc.0method: password
ui.login.successaudit logurn:qauth:event:user.signed_in:v1-rc.0method: password
ui.login.failureaudit logurn:qauth:event:user.sign_in_failed:v1-rc.0method: password
ui.login.csrf_failureaudit logurn:qauth:event:request.csrf_rejected:v1-rc.0form: sign_in
ui.wallet_login.successaudit logurn:qauth:event:user.signed_in:v1-rc.0method: wallet
ui.wallet_login.failureaudit logurn:qauth:event:user.sign_in_failed:v1-rc.0method: wallet
ui.wallet_login.csrf_failureaudit logurn:qauth:event:request.csrf_rejected:v1-rc.0form: wallet_sign_in
user.logout.successaudit logurn:qauth:event:user.signed_out:v1-rc.0—
user.logout.failureaudit logurn:qauth:event:user.sign_out_failed:v1-rc.0—
user.register.successlog lineurn:qauth:event:user.registered:v1-rc.0method: password
user.register.failurelog lineurn:qauth:event:user.registration_failed:v1-rc.0—
oauth.authorize.successaudit logurn:qauth:event:authorization.completed:v1-rc.0—
oauth.authorize.failureaudit logurn:qauth:event:authorization.failed:v1-rc.0—
oauth.stepup.requiredaudit logurn:qauth:event:step_up.required:v1-rc.0—
oauth.stepup.elevationaudit logurn:qauth:event:step_up.elevated:v1-rc.0—
oauth.consent.grantedaudit logurn:qauth:event:consent.granted:v1-rc.0—
oauth.consent.deniedaudit logurn:qauth:event:consent.denied:v1-rc.0—
oauth.consent.revokedaudit logurn:qauth:event:consent.revoked:v1-rc.0—
oauth.consent.csrf_failureaudit logurn:qauth:event:request.csrf_rejected:v1-rc.0form: consent
consents.revoke.csrf_failureaudit logurn:qauth:event:request.csrf_rejected:v1-rc.0form: consent_revocation
oauth.token.exchange.successaudit logurn:qauth:event:token.issued:v1-rc.0—
oauth.token.exchange.failureaudit logurn:qauth:event:token.request_failed:v1-rc.0—
oauth.revoke.successaudit logurn:qauth:event:token.revoked:v1-rc.0—
oauth.revoke.failureaudit logurn:qauth:event:token.revocation_failed:v1-rc.0—
oauth.introspect.successaudit logurn:qauth:event:token.introspected:v1-rc.0—
oauth.introspect.failureaudit logurn:qauth:event:token.introspection_failed:v1-rc.0—
oauth.userinfo.successaudit logurn:qauth:event:userinfo.served:v1-rc.0—
oauth.userinfo.failureaudit logurn:qauth:event:userinfo.request_failed:v1-rc.0—
oauth.client.registeredaudit logurn:qauth:event:client.created:v1-rc.0registration: dynamic
oauth.client.createdaudit logurn:qauth:event:client.created:v1-rc.0registration: developer_api
oauth.client.updatedaudit logurn:qauth:event:client.updated:v1-rc.0—
oauth.client.deletedaudit logurn:qauth:event:client.deleted:v1-rc.0—
oauth.client.secret_regeneratedaudit logurn:qauth:event:client.secret_rotated:v1-rc.0—
oauth.client.suspendedaudit logurn:qauth:event:client.suspended:v1-rc.0—
oauth.client.suspension_liftedaudit logurn:qauth:event:client.suspension_lifted:v1-rc.0—
api_key.createdaudit logurn:qauth:event:api_key.created:v1-rc.0—
api_key.revokedaudit logurn:qauth:event:api_key.revoked:v1-rc.0—
auth.wallet_link.successaudit logurn:qauth:event:wallet.linked:v1-rc.0—
auth.wallet_link.failureaudit logurn:qauth:event:wallet.link_failed:v1-rc.0—
auth.wallet_link.csrf_failureaudit logurn:qauth:event:request.csrf_rejected:v1-rc.0form: wallet_link
oid4vp.response.receivedaudit logurn:qauth:event:wallet.presentation_received:v1-rc.0—