Claims Reference
Reference for the scopes you can request and the verified claims CAIRL returns.
Overview
CAIRL returns verified claims — boolean or structured derived assertions about a user's identity — instead of raw identity material. The claims interface does not return raw documents, names, or biometric material. Consented derived assertions are still personal data, and recipients must handle them under applicable privacy, security, retention, and legal obligations. You receive only the specific claims you requested and the user consented to share.
Claims are returned in three places:
- The access token JWT under
cairl_claims - The
/api/oauth/userinfoendpoint response underclaims - The
claimsfield ofvae.resolvedwebhook events
CAIRL does not share the user's name, address, date of birth, ID document or photos, email address, or phone number through this interface. The consent screen lists those items explicitly as not shared.
Scopes vs. claim names
You request with a scope. You receive a claim.
scope=age:18+returnsclaims.age_18_plusscope=identity:verifiedreturnsclaims.identity_verifiedscope=identity:face_matchreturnsclaims.photo_verifiedscope=freshness:currentreturnsclaims.freshness_currentScopes use a colon and, for ages, a plus sign. Claim names use underscores. Putting a claim name in
scope(for examplescope=age_18_plus) is rejected withinvalid_scope.
Specify scopes using the scope parameter in the authorization URL. Multiple scopes are space-delimited:
scope=age:18+ identity:verifiedUsers see exactly what you request on CAIRL's consent screen. Request only what your application needs.
Scopes you can request today
Any value not in this table is rejected with invalid_scope.
Age
| Scope | Claim returned | Type | Description |
|---|---|---|---|
age:13+ | age_13_plus | boolean | User is 13 or older |
age:16+ | age_16_plus | boolean | User is 16 or older |
age:18+ | age_18_plus | boolean | User is 18 or older |
age:21+ | age_21_plus | boolean | User is 21 or older |
age:25+ | age_25_plus | boolean | User is 25 or older |
age:55+ | age_55_plus | boolean | User is 55 or older |
age:65+ | age_65_plus | boolean | User is 65 or older |
Age is derived from the verified government ID; the date of birth itself is not shared.
Identity
| Scope | Claim returned | Type | Description |
|---|---|---|---|
identity:verified | identity_verified | boolean | User completed full identity verification (document + face match) |
identity:face_match | photo_verified | boolean | User's live face matched the photo on their identity document |
Freshness
| Scope | Claim returned | Type | Description |
|---|---|---|---|
freshness:current | freshness_current | boolean | User's identity verification meets your configured freshness window |
Freshness policy is chosen per API key (Freshness Policy in the
Generate API Key dialog: monthly, biweekly, weekly, 3day,
daily, session, transaction). If the user's last full verification is
older than your policy window, freshness_current is false.
freshness_current reflects the time-based policies only — daily,
3day, weekly, biweekly, monthly. The event-driven policies, session
and transaction, are not enforced at /oauth/authorize today: a key on
either one passes the door regardless of when the person last verified. If you
need a per-session or per-transaction check, send the user through
/verify/start.
Jurisdiction-mode age assurance
Jurisdiction-mode scopes add policy-versioned receipt fields to an ordinary age claim. They are designed for regulated age-assurance programs where the site needs evidence of method class, threshold, retention posture, and policy version without receiving raw identity material.
Pair one jurisdiction mode with the matching base age scope:
scope=age:18+ age_assurance:tx| Scope | Pair with | Status |
|---|---|---|
age_assurance:tx | age:18+ | Available — Texas adult age-verification posture |
age_assurance:ut | age:18+ | Available — Utah adult age-verification posture |
age_assurance:ca | age:18+ | Available — California age-signal posture as rules phase in |
age_assurance:uk | age:18+ | Available — UK highly effective age-assurance support posture |
age_assurance:au | age:16+ | Available — Australia reasonable-steps support posture |
age_assurance:la | — | Under counsel review. Accepted as a scope but returns no receipt fields until counsel approves the final posture. |
When a jurisdiction mode is active and not on hold, CAIRL returns fields like:
{
"claims": {
"age_18_plus": true,
"age_assurance_tx_age_over_18": true,
"age_assurance_tx_age_over_16": true,
"age_assurance_tx_threshold_met": true,
"age_assurance_tx_decision_result": "pass",
"age_assurance_tx_jurisdiction": "tx",
"age_assurance_tx_age_threshold": 18,
"age_assurance_tx_age_bracket": "18_plus",
"age_assurance_tx_method_class": "government_id_liveness_face_match",
"age_assurance_tx_assurance_level": "standard",
"age_assurance_tx_retention_mode": "no_identifying_retention_after_access",
"age_assurance_tx_policy_version": "tx-hb1181-2026-06-22",
"age_assurance_tx_verified_at": "2026-06-22T12:00:00.000Z",
"age_assurance_tx_expires_at": "2026-07-22T12:00:00.000Z",
"age_assurance_tx_evidence_receipt_id": "aar_tx_0123456789abcdef01234567"
}
}The evidence receipt is a non-identifying reference derived from the pairwise subject, policy version, jurisdiction, and issuance timestamp. It is not a copy of the user's ID, date of birth, name, address, or document image.
Important limits:
- CAIRL provides policy-versioned age-assurance support, not a blanket legal compliance assurance.
- Your site's compliance still depends on your content, user flow, jurisdiction, retention choices, disclosures, fallback handling, and counsel-approved implementation.
- If a jurisdiction-mode scope is requested without its base age scope, the
receipt's
threshold_metisnullanddecision_resultis"unknown".
Identity Integrity Assurance (restricted pilot)
| Scope | Claim returned | Type | Status |
|---|---|---|---|
identity:integrity | identity_uniqueness_assurance | object | Restricted pilot. Rejected with invalid_scope unless enabled for your client. |
When issued, the claim is a bounded object:
{
"identity_uniqueness_assurance": {
"status": "established",
"methodVersion": "identity-integrity-v1",
"assessedAt": "2026-08-18T14:00:00.000Z",
"expiresAt": "2026-11-16T14:00:00.000Z"
}
}A keyless sandbox, where available, uses synthetic-demo-v1 to demonstrate this
wire contract. It is not production proof. Live issuance is
fail-off and the claim is omitted for shadow, pending, unavailable, expired,
revoked, or otherwise uncovered states. Identity Integrity Assurance is not yet
generally available; it must not be read as a promise of "one human per
account" or of global or lifetime coverage. Its presence for a pilot client is a
technical control and does not itself prove legal, fairness, or operations
clearance.
Planned (not yet requestable)
These claim names appear in CAIRL's internal claim registry but have no wire
scope today. Requesting them returns invalid_scope. They are listed so you
can plan, not so you can request them.
| Claim | Description |
|---|---|
document_active | User's identity document is not expired |
unique_per_partner | User has an active enrollment with your site |
name_match | Submitted name matches the verified name |
dob_match | Submitted date of birth matches the verified record |
address_match | Submitted address matches the verified record |
email_verified | User's CAIRL email is verified |
phone_verified | User's CAIRL phone number is verified |
The comparison claims (name_match, dob_match, address_match) would
require the user to submit data for CAIRL to compare; only the match result
would be returned. Watch the changelog for
availability.
Claim values
Userinfo returns requested claim values under the claims object:
{
"sub": "pws_v1_abc123...",
"evaluated_at": "2026-05-03T10:00:00.000Z",
"claims": {
"age_18_plus": true,
"identity_verified": true,
"freshness_current": false
}
}A claim is only false when that claim's contract defines failure as false. Unavailable evaluations follow the claim-specific contract: Identity Integrity Assurance is omitted; boolean eligibility claims may return their documented conservative value, and an age claim that could not be resolved may be null.
The sub field
Live token and userinfo responses include sub — a pseudonymous identifier scoped to your site. Test-mode responses return a random pws_v1_test_… value and sandbox responses use synthetic values; neither should be used to link sessions:
pws_v1_abc123def456...Properties of sub:
- Stable for your site — use the live
subto recognize a returning user for that integration - Site-scoped (pairwise) — it is designed to differ across sites that integrate CAIRL, reducing cross-site linkability
- Opaque to the recipient — it does not expose CAIRL's internal user identifier; treat it as personal data, not as anonymous data
Use the live sub as the pseudonymous key for returning users in your application's records.
Claims in test mode
With test credentials the flow itself is real — you sign in with your own CAIRL account and approve the consent screen — but the claim values are fixtures, filtered to the scopes you requested. The key's verification depth and freshness requirements are treated as satisfied, so a tester who has not completed identity verification still receives claims. Token and userinfo responses carry "mode": "test" so your code can tell fixture claims from verification results. The full split is in what is real and what is synthetic in test mode.
Claims and billing
| Scenario | Billing event | Price |
|---|---|---|
| First verification of a person on your site | Enrollment | $0.50, once per person |
| Every later check for that person | Verified Access Event | Priced from the claims evaluated, minimum $0.03 |
A VAE has no flat rate and no allowance of included claims. It costs the
sum of the claims it evaluates, floored at $0.03 — so the two claims used
throughout this guide, age:18+ and identity:verified, cost $0.06
together. Request more claims and the VAE costs more; this is the direct
reason to request only the claims you act on.
Billing occurs at the token exchange step — only when you successfully retrieve a token. Abandoned sessions and failed exchanges are not billed. Test credentials are not billed.
Evaluation timing
Claims are evaluated at token exchange, not at verification time. This means:
freshness_currentis evaluated against your key's freshness policy at exchange time- Age claims reflect the user's age on the day of the exchange
Userinfo returns the issuance-time snapshot
GET /api/oauth/userinfo returns the claim values as evaluated when your access token was issued — not a live re-evaluation against the user's current state.
Why
The user consented to a specific set of claim values at the moment of token exchange. If a user's underlying state changes after that moment (a verification expires, an attribute is corrected), CAIRL would silently disclose the new state on every subsequent userinfo call if it re-evaluated live. That would be a state change the user did not authorize you to observe at consent time.
The contract is point-in-time: the userinfo response represents the user's state at the moment the token was issued, and that contract holds for the lifetime of the token.
Practical consequences
- The
evaluated_atfield in the userinfo response is the token issuance timestamp, not the time of the userinfo call. For a given token, this value is stable across requests. - A backend mutation (verification renewal, attribute change) between token issuance and your userinfo call does not change the values returned by userinfo for that token.
- To get fresh claims, request a new token via the standard authorization flow. The new token captures a new snapshot at its issuance time. This is also the consent-renewal point.
What does NOT change
- Token revocation continues to work as before — a revoked token returns
401from userinfo regardless of what the snapshot contains. See the token revocation docs. - Token expiry is enforced as before. Snapshot semantics apply only to the lifetime of an unrevoked, unexpired token.
Response shape
{
"sub": "pws_v1_<pairwise>",
"evaluated_at": "2026-05-03T10:00:00.000Z",
"claims": {
"age_18_plus": true,
"identity_verified": true
},
"meta": {
"claims_requested": ["age_18_plus", "identity_verified"],
"claims_resolved": ["age_18_plus", "identity_verified"],
"claims_null": [],
"claims_ignored": []
}
}meta.claims_ignored is reserved for future use; treat its absence as "the evaluator did not flag any claims as ignored at issuance."
Adding claims after launch
You can request additional claims by updating the scope parameter in your authorization URL. Users will see the new claims on the consent screen the next time they authenticate. For live keys, the scopes you request must also be selected under Required Claims for that key on /home/f/{slug}/keys.