API Reference
Token Validation
Validate an AgeOnce access token
Token Validation
Validate an access_token with the AgeOnce API, or locally with the public key.
Endpoint
GET https://api.ageonce.com/api/verify/token/{access_token}The token is a path parameter. URL-encode it.
Response
Valid token (200)
{
"success": true,
"valid": true,
"age_verified": true,
"age_over": 18,
"verification_level": "first_verification",
"expires_at": "2026-02-11T12:10:00.000Z",
"issued_at": "2026-02-11T12:00:00.000Z",
"session_id": "user-id",
"client_id": "ap_live_your_client_id",
"nonce": null,
"transaction_id": "550e8400-e29b-41d4-a716-446655440000"
}client_id is the JWT aud claim. transaction_id is the same Audit ID returned by token exchange. Search it in Dashboard Audit Logs.
Invalid or expired token (401)
{
"success": false,
"valid": false,
"message": "Invalid or expired token"
}Examples
curl "https://api.ageonce.com/api/verify/token/YOUR_ACCESS_TOKEN"async function validateToken(token) {
const response = await fetch(
`https://api.ageonce.com/api/verify/token/${encodeURIComponent(token)}`
);
const data = await response.json();
if (response.ok && data.valid && data.age_verified) {
console.log('Verified over:', data.age_over);
return true;
}
console.log('Validation failed:', data.message);
return false;
}import requests
from urllib.parse import quote
def validate_token(token):
response = requests.get(
f"https://api.ageonce.com/api/verify/token/{quote(token, safe='')}"
)
data = response.json()
if response.ok and data.get('valid') and data.get('age_verified'):
print(f"Verified over: {data['age_over']}")
return True
print(f"Validation failed: {data.get('message')}")
return Falsefunction validateToken($token) {
$url = 'https://api.ageonce.com/api/verify/token/' . rawurlencode($token);
$response = file_get_contents($url);
$data = json_decode($response, true);
if (!empty($data['valid']) && !empty($data['age_verified'])) {
echo 'Verified over: ' . $data['age_over'];
return true;
}
echo 'Validation failed: ' . ($data['message'] ?? 'Unknown error');
return false;
}API vs local validation
| Aspect | API validation | Local validation |
|---|---|---|
| Speed | One HTTP request | Faster after the key is cached |
| Simplicity | Read age_over from JSON | Verify the RS256 signature yourself |
| Key | Server uses the current key | Cache JWKS or GET /api/verify/public-key |
For high-load checks, validate locally and cache the public key.
Local validation
GET /api/verify/public-key returns the PEM public key. Verify with RS256 and issuer https://ageonce.io. age_over is the verified age threshold.
const jwt = require('jsonwebtoken');
async function validateLocally(token) {
const keyResponse = await fetch('https://api.ageonce.com/api/verify/public-key');
const { public_key: publicKey } = await keyResponse.json();
const payload = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'https://ageonce.io',
});
return payload.age_verified === true && payload.age_over >= 18;
}The same key is published as JWKS at GET /api/.well-known/jwks.json. Issued tokens do not include kid, so use that single key rather than looking up a header key id.