Use AgeKey Authorization
The Use AgeKey authorization endpoint initiates the age verification flow for users who already have an AgeKey. Your application should redirect the user's browser to this endpoint with the appropriate parameters to start the verification process.
This endpoint implements the OpenID Connect Implicit Flow with id_token response type.
Authorization endpoint
- cURL
- JavaScript
- Python
curl -X GET "https://api.agekey.org/v1/oidc/use" \
-G \
-d "scope=openid" \
-d "response_type=id_token" \
-d "client_id=your-client-id" \
-d "redirect_uri=https://yourapp.com/agekey/callback" \
-d "state=abc123xyz789" \
-d "nonce=nonce456def" \
-d "claims=%7B%22age_thresholds%22%3A%5B13%2C18%5D%7D"
const params = new URLSearchParams({
scope: 'openid',
response_type: 'id_token',
client_id: 'your-client-id',
redirect_uri: 'https://yourapp.com/agekey/callback',
state: 'abc123xyz789',
nonce: 'nonce456def',
claims: JSON.stringify({ age_thresholds: [13, 18] })
});
const authUrl = `https://api.agekey.org/v1/oidc/use?${params.toString()}`;
window.location.href = authUrl;
from urllib.parse import urlencode
import json
params = {
'scope': 'openid',
'response_type': 'id_token',
'client_id': 'your-client-id',
'redirect_uri': 'https://yourapp.com/agekey/callback',
'state': 'abc123xyz789',
'nonce': 'nonce456def',
'claims': json.dumps({'age_thresholds': [13, 18]})
}
auth_url = f"https://api.agekey.org/v1/oidc/use?{urlencode(params)}"
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
scope | string | Yes | Always set to openid, optionally can also set agekey.upgrade if upgrades are allowed |
response_type | string | Yes | Always set to id_token |
response_mode | string | No | How the authorization response is returned to your redirect_uri: fragment (default), query, or form_post. |
client_id | string | Yes | Your AgeKey client ID |
redirect_uri | string | Yes | Where users return after verification |
state | string | Yes | Client-generated value for CSRF protection and maintaining application state |
nonce | string | Yes | Random value for replay protection |
claims | string | Yes | URL-encoded JSON specifying age thresholds and other constraints |
can_create | string | No | When present and true, users see both "Use AgeKey" and "Create AgeKey". When omitted or not true, only "Use AgeKey" is shown. |
language | string | No | Optional IETF BCP 47 language tag (for example en-US, pt-BR) to set the AgeKey UI language for this redirect. When omitted, the UI follows the visitor's browser language preferences. Applies to use and upgrade flows, which both use this authorization URL. |
theme | string | No | Sets the visual theme of the AgeKey UI for this redirect. Accepts light, dark, or system. When omitted, the UI defaults to light. Applies to use and upgrade flows, which both use this authorization URL. |
Claims parameter
The claims parameter must be a URL-encoded JSON object specifying which age thresholds to verify and filtering criteria:
| Field | Type | Required | Description |
|---|---|---|---|
age_thresholds | array | Yes | Array of age thresholds to verify (for example [13, 18, 21]) |
allowed_methods | array | No | Array of verification methods to accept. (for example ["id_doc_scan", "payment_card_network"]) All verification methods are considered when not provided. |
verified_after | string | No | ISO 8601 date/datetime - only accept verifications after this date |
provenance | object | No | Filter by verification provider origin (see provenance structure below) |
overrides | object | No | Method-specific filtering rules (see overrides structure below) |
iso_27566_1 | string | No | Minimum ISO 27566-1 level of effectiveness an age signal must meet (see ISO 27566-1 filtering below) |
Provenance structure:
Provenance filtering allows you to control which verification providers are accepted based on their origin path. Each provider has a unique provenance path that identifies the verification technology used.
| Field | Type | Description |
|---|---|---|
allowed | array | Allowed provenance patterns. Use * suffix for prefix matching (for example /veratad/* matches all Veratad providers) |
denied | array | Denied provenance patterns. Takes precedence over allowed. Use to exclude specific providers |
Pattern matching:
- Patterns must start with
/and use lowercase letters, numbers, and underscores - Use
/*suffix for prefix matching (for example/veratad/*matches/veratad/internal,/veratad/trinsic, and other paths under that prefix) - Exact paths match only that specific provider (for example
/stripeonly matches Stripe) - Maximum 10 patterns per array, each up to 100 characters
Example with provenance filtering:
{
"age_thresholds": [18],
"provenance": {
"allowed": ["/veratad/*", "/stripe", "/singpass"],
"denied": ["/veratad/internal"]
}
}
In this example:
- All Veratad providers are allowed via the
/veratad/*wildcard - Stripe and Singpass verifications are explicitly allowed
- Veratad internal verifications are excluded (denied takes precedence over allowed)
Available provenances:
| Provenance | Description |
|---|---|
/connect_id | Connect ID verification |
/stripe | Stripe identity verification |
/inicis | Inicis verification (Korea) |
/singpass | Singpass verification (Singapore) |
/privy | Privy verification |
/spruce_id | Spruce ID verification |
/verify_my | VerifyMy verification |
/privately | Privately verification |
/veratad/internal | Veratad internal verification |
/veratad/trinsic | Veratad via Trinsic |
/veratad/cra | Veratad via CRA verification |
/veratad/roc | Veratad via ROC verification |
/yoti | Yoti verification |
/meta | Meta verification |
/friendly_id/amazon | Friendly ID via Amazon |
/paymentico | Paymentico verification |
ISO 27566-1 filtering
The iso_27566_1 field filters age signals by their certified ISO 27566-1 level of effectiveness, an independent measure of how rigorously an age assurance method was performed, separate from the verification method itself.
| Field | Type | Required | Description |
|---|---|---|---|
iso_27566_1 | string | No | Minimum required level of effectiveness. Only signals certified at this level or higher are accepted. |
Levels of effectiveness, in ascending order of rigor:
| Value | Description |
|---|---|
basic | Accepts any ISO 27566-1 certified age signal |
effective | Requires at least "effective" certification |
highly_effective | Requires at least "highly effective" certification |
strict | Requires the highest ("strict") certification level |
Signals that aren't ISO 27566-1 certified at all always fail this filter, regardless of the level requested. When iso_27566_1 is omitted, no level-of-effectiveness filtering is applied.
Example:
{
"age_thresholds": [18],
"iso_27566_1": "highly_effective"
}
This accepts only age signals certified as highly_effective or strict.
Per-method overrides:
iso_27566_1 can also be set inside overrides.<method> to require a different level of effectiveness for a specific verification method. A method-level override takes precedence over the root iso_27566_1 value for that method:
{
"age_thresholds": [18],
"allowed_methods": ["id_doc_scan", "facial_age_estimation"],
"iso_27566_1": "effective",
"overrides": {
"facial_age_estimation": {
"min_age": 18,
"iso_27566_1": "strict"
}
}
}
Here, id_doc_scan signals need only effective or higher, while facial_age_estimation signals must meet the stricter strict level.
Overrides structure:
Each method key inside overrides accepts the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
min_age | integer | No* | A single minimum age threshold for this method (0–150). For facial_age_estimation, exactly one of min_age or age_thresholds is required. |
age_thresholds | array | No* | Per-threshold overrides that map 1:1 by index to the root age_thresholds. Each entry is the minimum age (for this method) required to satisfy the corresponding root threshold. Length must equal root age_thresholds length. |
verified_after | string | No | Override verified_after for this specific method |
iso_27566_1 | string | No | Minimum ISO 27566-1 level of effectiveness for this method (basic, effective, highly_effective, or strict). Takes precedence over the root iso_27566_1 value. |
attributes | object | No | Method-specific attribute requirements |
*For facial_age_estimation, one of min_age or age_thresholds must be provided.
Example with method and attribute filtering:
{
"age_thresholds": [13, 18],
"allowed_methods": ["id_doc_scan", "payment_card_network"],
"verified_after": "2024-01-01",
"overrides": {
"id_doc_scan": {
"attributes": {
"issuing_country": ["US", "GB"],
"face_match_performed": true
}
},
"payment_card_network": {
"attributes": {
"card_type": ["credit"]
}
}
}
}
Example with age_thresholds override for facial age estimation:
{
"age_thresholds": [13, 18],
"overrides": {
"facial_age_estimation": {
"age_thresholds": [16, 21]
}
}
}
In this example, root threshold 13 requires the facial age estimation to report at least 16, and root threshold 18 requires at least 21.
Example with min_age override for facial age estimation:
{
"age_thresholds": [18],
"overrides": {
"facial_age_estimation": {
"min_age": 21
}
}
}
Response
On success, users are redirected to your redirect_uri with an id_token, state, and potentially a code in the URL fragment:
https://yourapp.com/agekey/callback#
id_token=eyJhbGc...long-jwt-string...&
state=abc123xyz789
When you sent can_create=true and the user was shown the use-or-create dialog and chose to create an AgeKey, they're redirected with create_requested=true and no id_token. Your app should send them to complete verification with another method (such as ID document scan). Once that verification succeeds, direct them to the Create AgeKey flow.
https://yourapp.com/agekey/callback#
state=abc123xyz789&
create_requested=true
| Parameter | When present | Description |
|---|---|---|
id_token | Success | Signed JWT with age threshold results. Must be validated on your server before trusting the results. |
state | Always | The state value you sent; verify it for CSRF protection. |
code | With agekey_upgrade scope | Returned when using the agekey_upgrade scope; can be exchanged to upgrade an AgeKey with a new verification. |
create_requested | When can_create=true was sent | Present and true when the user chose "Create AgeKey" in the use-or-create dialog. Send the user to complete verification with another method; after success, direct them to the Create AgeKey flow. No id_token is returned. Only returned if the request included can_create=true. |
error | On error | Error code (for example, access_denied when the user clicks back or abandons the flow); handle appropriately. |
The id_token contains age threshold results and must be validated on your server before trusting the results.
The code is returned when using the agekey.upgrade scope and can be used to upgrade an AgeKey with a new verification.
ID token claims
The decoded id_token JWT payload contains the following claims:
| Claim | Type | Description |
|---|---|---|
sub | string | Session ID. A unique identifier for this verification session. You should log this value for invalidation auditing (see note below). |
iss | string | Token issuer. Always https://api.agekey.org/v1/oidc/use. |
aud | array | Audience. Contains your client_id. |
iat | number | Issued-at timestamp (UNIX epoch seconds). |
exp | number | Expiration timestamp (UNIX epoch seconds). Token is valid for 10 minutes. |
nonce | string | The nonce value from your authorization request. Must match to prevent replay attacks. |
age_thresholds | object | Map of requested threshold to boolean result. For example { "13": true, "18": false }. |
req_claims_hash | string | Base64url-encoded SHA-256 hash of the claims JSON you sent, for request integrity verification. |
c_hash | string | (Only with agekey.upgrade scope) Code hash per OIDC spec §3.3.2.11. |
The sub claim is the session ID for this verification. Persist this value in your system alongside the verification outcome. If an age signal is later revoked or found to be fraudulent, this session ID is used for invalidation auditing to identify which verifications were affected. Without it, impacted sessions can't be traced back to your users.