Scopes reference
The complete catalog of identity claims you can request. Mark each as Required, Optional, or Skip on the developer dashboard — that's the manifest the consent screen renders.
Standard OIDC scopes
- openid
- Required on every request. Activates OIDC mode.
- profile
- Compatibility alias for
wa:name.full+wa:photo. Use the explicit scopes instead — they give the user better control over what they share. - Not honored. Whisp3r Auth does not release the user's email address. Use email relay instead.
Whisp3r-specific scopes
All prefixed with wa:.
wa:photo
Profile photo. Available as picture in userinfo — the user's
photo URL directly (an absolute URL with a ?v=<timestamp> cache-buster that changes on every upload),
or null when they have no photo. The avatar container
(avatar@<handle>) carries the same URL on its url field, and a user.photo.changed webhook fires (to apps holding wa:photo) on every replace or remove.
wa:photo does
NOT guarantee the user has uploaded a photo. The picture claim, the url field on the avatar
container, and the body.url field on directory.avatar.updated are all null when the
user has cleared their photo (or never set one). Your app MUST render a
fallback in that case — initials, gradient, default mark, or the
default avatar of your choice. The platform does not ship a
deterministic-generated-avatar URL on null.wa:name.first
Just the first name. Available as given_name.
wa:name.full
The user's first, middle, last, and suffix. Implies wa:name.first (redundant on the consent screen if both requested).
The components ride along with the composed display name, so a consumer
can render name directly or pull just the family name without
re-requesting:
- OIDC userinfo claims:
name,given_name,middle_name,family_name,suffix - Directory resolve (
GET /v1/c/profile@<handle>with the app's access token):displayName,firstName,middleName,lastName,suffix - Webhook
directory.profile.updated: the same field set, filtered to whatever changed
wa:username
The user's Whisp3r Auth handle. Exposed as the preferred_username userinfo claim and the username field on the profile container.
Unique platform-wide; renames propagate via directory.profile.updated.
Rename + lineage
Handles aren't permanent identifiers — the sub claim is.
A user may rename their handle at most once every 30 days. When they
do, the abandoned handle enters a 30-day freeze window:
- The previous owner may reclaim it inside the window (the rename cooldown does not apply to reclaims).
- Nobody else may claim the handle while it is frozen.
GET /v1/c/profile@<old-handle>andGET /v1/c/avatar@<old-handle>return a 301 redirect to the user's current canonical alias throughout the window. Public inbound links keep working.- After the window expires the row is purged and the handle becomes publicly claimable again.
On every rename, directory.profile.updated carries a previous_username field so apps can remap their local
denormalized copy of the handle to the new value. The sub stays the same — apps key by it for identity continuity.
wa:pronouns
The pronouns the user has chosen — e.g. she/her. Available as pronouns.
wa:age.tiers
Yes/no checks for whether the user is at least 13, 16, 18, or 21. The actual birthday never leaves Whisp3r Auth under this scope.
"age_at_least_13": true,
"age_at_least_16": true,
"age_at_least_18": true,
"age_at_least_21": false,
"age_verification": {
"verified": false,
"method": null,
"verified_at": null
} age_verification tells you whether the underlying
age is attested or self-reported.
Today the field always returns { verified: false, method: null, verified_at: null } because the only source for the age is the user's
self-reported birthday. When an ID-document verification flow
(Persona, Stripe Identity, etc.) ships, completed verifications
flip verified to true and stamp the
method + timestamp. Apps that need an attested age — for legal /
compliance reasons — should gate features on age_verification.verified === true.
Dispatch is deterministic. Birthday changes that move the user
across a tier boundary trigger an event delivery to wa:age.tiers subscribers; changes that don't cross a
boundary (a one-day correction, etc.) do not — same booleans
before and after, no notification.
wa:birthday
The exact birthday — month, day, year. Sensitive. The
consent screen calls this out specifically. Only ask if your app
genuinely needs it (insurance, regulated KYC); otherwise use wa:age.tiers. Implies wa:age.tiers.
"birthdate": "1990-05-12",
"birthdate_verification": {
"verified": false,
"method": null,
"verified_at": null
} Same verification semantics as age_verification — the birthdate_verification.verified flag flips when a
verified ID document's birthday matches the stored value.
wa:language
The user's preferred interface language as a BCP-47 tag — "en-US", "fr", "es-MX", etc.
Available as locale. Falls back to null if the user hasn't set one.
wa:cookies
The user's analytics + marketing cookie opt-ins. Available as cookie_preferences:
"cookie_preferences": {
"analytics": false,
"marketing": true
} Respect these. The user opted in once at Whisp3r Auth; if your app re-prompts them, you erode the trust this platform is selling.
Auto-granted scopes
Hidden from the consent screen. Granted to every authorized app.
- wa:email.relay
- Lets your app POST to /api/relay/email to send a user a message without learning their address.
- wa:webhook
- Lets your registered webhook URL receive signed events when a granted field changes. See Webhooks.
Scope dependency rules
Some scopes imply others. Requesting a "stronger" scope is treated as also requesting the weaker form:
- wa:name.full →
- wa:name.first
- wa:birthday →
- wa:age.tiers
The developer dashboard reflects this in the picker (toggling a parent required locks its implied child to required too). The consent screen hides the implied child from the user — they only see the parent decision.