CAIRLDocs
Integration

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/userinfo endpoint response under claims
  • The claims field of vae.resolved webhook 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+ returns claims.age_18_plus
  • scope=identity:verified returns claims.identity_verified
  • scope=identity:face_match returns claims.photo_verified
  • scope=freshness:current returns claims.freshness_current

Scopes use a colon and, for ages, a plus sign. Claim names use underscores. Putting a claim name in scope (for example scope=age_18_plus) is rejected with invalid_scope.

Specify scopes using the scope parameter in the authorization URL. Multiple scopes are space-delimited:

scope=age:18+ identity:verified

Users 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

ScopeClaim returnedTypeDescription
age:13+age_13_plusbooleanUser is 13 or older
age:16+age_16_plusbooleanUser is 16 or older
age:18+age_18_plusbooleanUser is 18 or older
age:21+age_21_plusbooleanUser is 21 or older
age:25+age_25_plusbooleanUser is 25 or older
age:55+age_55_plusbooleanUser is 55 or older
age:65+age_65_plusbooleanUser is 65 or older

Age is derived from the verified government ID; the date of birth itself is not shared.

Identity

ScopeClaim returnedTypeDescription
identity:verifiedidentity_verifiedbooleanUser completed full identity verification (document + face match)
identity:face_matchphoto_verifiedbooleanUser's live face matched the photo on their identity document

Freshness

ScopeClaim returnedTypeDescription
freshness:currentfreshness_currentbooleanUser'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
ScopePair withStatus
age_assurance:txage:18+Available — Texas adult age-verification posture
age_assurance:utage:18+Available — Utah adult age-verification posture
age_assurance:caage:18+Available — California age-signal posture as rules phase in
age_assurance:ukage:18+Available — UK highly effective age-assurance support posture
age_assurance:auage:16+Available — Australia reasonable-steps support posture
age_assurance:laUnder 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_met is null and decision_result is "unknown".

Identity Integrity Assurance (restricted pilot)

ScopeClaim returnedTypeStatus
identity:integrityidentity_uniqueness_assuranceobjectRestricted 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.

ClaimDescription
document_activeUser's identity document is not expired
unique_per_partnerUser has an active enrollment with your site
name_matchSubmitted name matches the verified name
dob_matchSubmitted date of birth matches the verified record
address_matchSubmitted address matches the verified record
email_verifiedUser's CAIRL email is verified
phone_verifiedUser'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 sub to 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

ScenarioBilling eventPrice
First verification of a person on your siteEnrollment$0.50, once per person
Every later check for that personVerified Access EventPriced 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_current is 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_at field 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 401 from 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.

On this page